Skip to main content

Draw a crowd with Mass

Part of the SDK since v2.19.0.

Since v2.19.0 CrowdyMass is part of the SDK; on v2.18.0 and earlier it was the separate CrowdyMass plugin, as What's Changed explains.

Crowds with Mass is a way to draw many remote players, or other Dynamic entities, without spawning an actor for each one. The other players on a map become rows of a crowd that the engine draws and animates in bulk. This page tells you what that means, when it is the right choice, and where to read next. If you learn by doing, follow Market night in Oakford.

What it is​

Crowds with Mass is a rendering backend. A rendering backend decides what a machine puts on screen for an entity that belongs to someone else. The default backend, the actor pool, spawns a real actor of the entity's class. The Crowdy Mass Rendering Backend (UCrowdyMassRenderingBackend) draws the same entity as one row of a crowd instead: a position, an animation state and a few numbers in a table that Unreal's Mass framework updates in bulk and draws as instanced skinned meshes. No actor exists for that row.

A crowd of pale animated mannequins walking and jogging across a tiled floor, every one of them drawn as a Mass crowd row rather than as an actor

You do not need to know Mass to use it. Everything here is Blueprint and assets except three jobs that need C++: registering the fields a graph reads by name, writing your own fragment, trait and processor, and registering an event handler that adds an effect while the row keeps playing its action. Add your own crowd data, Read and write its fields, from Blueprint and Receive its events, from C++ say where.

Crowds with Mass is part of the CrowdySDK plugin. There is nothing to install and nothing to enable beyond the plugin you already use. A map opts in by choosing the backend on its map profile.

Your own player is always a real actor.

Every player is a real actor on their own machine, whatever backend the map uses. A crowd row is how someone else's machine draws that player, never how you see yourself.

Why you would use it​

The actor pool spawns one actor for every remote entity in range. Each actor carries its components, its animation graph and its tick, which is the right cost for a few players and the wrong one for a square full of them.

A crowd row costs a table entry instead of an actor. It draws through an instanced skinned mesh. It plays its animation from a bank shared by every row of the same character. A few hundred to a few thousand remote players can stay on screen at a steady frame rate.

On market night in Oakford the village square fills with players, far more than the actor pool should draw. The Oakford map profile selects Mass, and every other player in the square is a crowd row.

When not to use it​

  • You expect few remote entities. With a dozen or so players in range, the actor pool is simpler and costs you no extra authoring.
  • Every remote entity must be a full actor everywhere. A crowd row has no components, no physics body of its own and no animation graph. Code that lives on the actor does not run for a row. An event body runs on a stand-in instead, and it has limits; see Run your code on a crowd row.
  • You want full actors only up close. That is possible: a character can opt into a near tier, and the closest entities, up to a ceiling you set, are drawn as real actors. See Promote close entities to actors. Everything beyond the ceiling stays a row.
QuestionActor poolCrowds with Mass
How you choose itIt is the default; set nothingSet Backend Class on the map profile
How a remote entity is drawnA pooled actor of its classA crowd row; an actor only when close, and only for a class that opts in
What the cost grows withThe number of actorsThe number of rows, each far cheaper than an actor
AnimationThe actor's own animation graphAn anim set and bank you author, shared by every row
Your code on a remote entityRuns on the actor as usualAn event body runs on a per-class stand-in; component code does not run
A good fit forTens of remote entitiesHundreds to thousands
Extra workNoneA flagged material, an anim set, optionally a near tier shell

Where it sits​

The backend is chosen per map, by the Backend Class on the map profile. A map that chooses nothing runs on the actor pool, and a project can use the actor pool on one map and Mass on another. Choosing Mass changes how remote entities are drawn; it does not change how they are sent, found or owned. Entity identity, Crowdy State, events and continuous state work the same way under both backends.

Turn Mass on for a map shows the setting.

Three ways one entity is drawn​

One entity can be drawn three ways on one machine, and every call you author has to be correct on all three.

RepresentationWhenWhat runs your code
A real actorAlways for the player on their own machine; for every entity everywhere, on an actor pool mapYour actor, as usual
A near tier actorA row that comes within Near Tier Distance of the camera, for a class that opted inThe per-class stand-in, as for a row; the near tier actor only draws the entity and plays its actions
A crowd rowThe default for a remote entity under MassFragments and processors, and the per-class stand-in for event bodies; no actor exists

A near tier actor is a pooled copy of the shell you build, bound to one entity while it is close. The stand-in is an object the SDK makes for each class to run your event bodies on when no real actor exists.

Author once, on the actor class​

You never author a "Mass version" of your character. You author the character, and on another machine the SDK draws it as a row. Everything is declared once, on your own actor class:

  • the Crowdy entity component and the state it sends, on ALanternPlayer;
  • the replicated Stance property that drives a pose, and the WaveTorch event that drives an action;
  • the anim set the crowd animates from, named by a component on the class;
  • the near tier shell, if you want close players drawn as actors.

Two planes, kept apart​

The SDK keeps two kinds of state and Crowds with Mass does not mix them.

View stateServer values
What it isCrowdy State, continuous state and eventsA Server Object, with a record per entity
Owned byThe client being observedThe server
CarriesMovement, stance, animation, anything cosmeticHealth, stats, inventory, anything that decides an outcome
TrustForgeableAuthoritative
Reaches a crowd row byA field you register, then a fragment on the rowA read from the entity's record, on demand
View state decides what is drawn. Server values decide what is true.

Anything a peer sends about itself can be forged, so a crowd row may use it to look right and never to decide an outcome. Put anything a peer must not be able to influence in a Server Object. A row reads those values in place; see Read its server values, from Blueprint and Read its server values, from C++, and Keep a record per entity for the Server Object itself.

The example in this section​

The pages follow one world, the village of Oakford. Players are ALanternPlayer, a character that sends FLanternPlayerState through its own executor, ULanternPlayerExecutor; BP_LanternPlayer is your Blueprint subclass of it. On market night every other player in the square is drawn as a crowd row. The raiders on the north road, from Raiders on the north road, show server values on rows. Market night in Oakford puts the whole thing together.

Before you start​

You need a signed-in project with a Dynamic Crowdy entity, usually the player pawn, and a character with a skeletal mesh: a row is built from that mesh, so a bare Character with no mesh produces no row. The Entity Component page lists the entity settings, and Continuous State builds the custom executor that ALanternPlayer uses. The Quickstart gets a project connected first.

Read this section in order​

  1. Turn Mass on for a map: choose the backend, set its options and project settings, flag the material.
  2. Make a character draw as a crowd row: the entity component, the state struct, the mesh and where the entity config comes from.
  3. Set up the animation set: the bank, the Crowdy Mass Anim Set, gaits and the rule that an index must change.
  4. Play poses and one-shot actions: a replicated enum becomes a held pose, and an event becomes a one-shot action.
  5. Promote close entities to actors: the near tier shell, pooling and distances.
  6. Run your code on a crowd row: what an event body does when the observer holds a row.
  7. Add your own crowd data: your own fragment, trait and processor, and the state fields that feed them.
  8. From Blueprint: Target an entity, Call an event on it, Receive its events, Read and write its fields, Read its server values.
  9. From C++: the same five pages, Target an entity, Call an event on it, Receive its events, Read and write its fields and Read its server values.
  10. Fix a crowd that does not look right: symptoms first, then the diagnostics you may use.
  11. Market night in Oakford: the worked example from end to end.