2,000 tickers: managers, subsystems, instancing
2,000 ticking actors cost scheduling, a call each and cache misses, and the actors themselves are expensive before they tick. A central dispatcher saves the scheduling. The real win is a manager that does the work itself in a tight loop over plain data, lives in a tickable world subsystem, and draws with instancing. At huge scale, use Mass.
Why 2,000 individual ticks is worse
- Each is an
FTickFunctionthe tick manager schedules every frame (sorting, prerequisites, dispatch), plus a virtual call, plus a Blueprint VM entry for BP ticks. - Actors are scattered in memory, so there's a cache miss on every one.
- Often most of the cost is the actors themselves: components, transforms, registration.
Your Game State dispatcher: what's good, what gets pushed on
| Good | Interviewer push |
|---|---|
| One tick function instead of 2,000 | A broadcast still makes 2,000 Blueprint calls. You saved scheduling, not per-call cost |
| Everyone updates at one controllable point in the frame | Order between listeners isn't guaranteed |
| Objects subscribe only while they need updates | Game State is a gameplay/replication class, so using it for engine plumbing is a coupling smell. And binding to a BP Game State hard-references it (references) |
The better versions
- Move the work into the manager: an array of structs (position, velocity), one C++ loop. That's cache friendly, with no calls out.
- Home it in a
UTickableWorldSubsystem: world lifetime, no actor, no hard reference (GetWorld()->GetSubsystem<T>()), ticks itself. - Don't have 2,000 actors: one manager owning instance data plus ISM/HISM for rendering.
- Very large counts: Mass Entity (entities, fragments, processors).
"Per-actor ticks each cost scheduling and a call, with bad cache locality, and 2,000 actors is expensive before they even tick. I've used a central dispatcher so objects only subscribe while they need updates. The better version moves the work into the manager: one tick in a tickable world subsystem, looping over a data array, with instanced rendering. At very large scale, Mass. It's the same idea as my plugin's cluster component: many shapes in one component."
Quiz yourself
Three reasons 2,000 ticking actors are slow.
Per-tick scheduling overhead, a call (and BP VM entry) per tick, scattered memory (cache misses). Plus the actors' own overhead.
What does a broadcast dispatcher save, and what doesn't it save?
Saves tick scheduling (one tick function). Doesn't save the per-listener call cost.
Where should a tick manager live, and why not Game State?
UTickableWorldSubsystem: world lifetime, no actor, no BP hard refs. Game State is for gameplay/replicated state, so using it here couples unrelated things.
What's ISM/HISM for?
Instanced Static Mesh (Hierarchical adds per-cluster culling/LOD): drawing many copies of a mesh in few draw calls, owned by one component.
How does this connect to your plugin?
The cluster component is one component owning many shapes instead of many components: the same "one owner, many items" idea.