Gessa Docs
Recipes

Recipe

Manager scripts driving many objects from one system

You have a crowd, a swarm, a field of pickups, or a wave of enemies that all behave the same. Put ONE manager script on ONE entity that drives the whole cohort, instead of attaching a script to every member. This is the default shape for a homogeneous population.
engine v1.0.234since script-semantic-patch-ops.v1, action-catalog.v1.0.232, world.v1, spawn.v1, mutation.v1, persistence.v1Copy for LLM

Use this for

one server-side script that controls a whole population of objects; a spawner or director that drives many behavior-less entities; central orchestration instead of a script per entity; batch updates over a group

Not for

a single self-driven mover (see moving-platforms-and-elevators); per-player input behavior (see input-mapping-actions-bindings); the match phase and scoreboard (see match-flow-and-scoreboards)

Pairs with: Timers countdowns cooldowns and enemy waves, Spawn points checkpoints and respawn flow, NPC and enemy navigation chase and patrol behavior, Match flow phases scoreboards and win conditions

The recipe

Author with project_apply_script_semantic_patch using the ai_safe op pack. The manager is an entity with a ScriptComponent.

  1. Tag the cohort: give every member a shared tag (for example mob) so one query finds them all.
  2. Enumerate on a beat: addTickHandler { "handlerKey":"onManage" } (or a coarser addTimerHandler for slow updates). Inside, findEntitiesByTag { "tag":"mob", "assignTo":"mobs", "limit": 64 }, or use the paged ctx.world.query in a GessaScript body for larger sets (pass its nextCursor back verbatim to page).
  3. Act on each member: forEachEntityInResult { "arrayRef":"mobs" } whose body uses only the loop-intent ops (despawnEntity, emitCustomEvent, incrementNumericState, attachComponentWithValidatedDefaults). For richer per-member work (move, retarget), iterate in a GessaScript body and call ctx.entity.patchComponent on each.
  4. Spawn and cull centrally: keep the population at a target size by spawnEntityFromTemplate when short and despawnEntity the excess or the dead, all from the one manager.
  5. Shared state: hold cohort-level counters (alive count, wave number) in durable state with incrementNumericState so the manager and the HUD read one truth.

Pitfalls

  • forEachEntityInResult.body accepts ONLY the four loop-intent ops; anything richer must run in a replaceScriptFromGessaScript body iterating the result.
  • Bound every scan: cap findEntitiesByTag limit and forEachEntityInResult maxIterations (both cap at 1000) and page ctx.world.query rather than pulling everything each tick.
  • The collision and per-tick budgets cap at 1000 entities; a manager that keeps spawning without culling will hit the ceiling.
  • Run the manager on the authority; do not also run a client copy or the cohort double-updates.

Verify

  • simulation_run (qa.run.start) to watch the manager spawn, update, and cull the cohort over several ticks.
  • project_get_graph_snapshot to confirm the single manager ScriptComponent and its tick or timer handler.
Was this helpful?Report an issueContact support

On this page