Spawning

Place entities in the world from the spawns collection, spawn.request events and scenes: spawn points, a cap on live entities, timed respawn, hooks to change or veto a spawn.

Source: module-kits:kits/spawn_system/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs spawn-systemMCP tool: gripforge_docs { "topic": "spawn-system" }

Spawning entities from data (spawn.system) #

What it is for #

spawn.system is the one door through which a game puts entities into the world from data. Each spawn entry names a kind (a vehicle, an NPC, a prop, a pickup...), a place and some tags; the kit that registered a factory for that kind builds the entity, and a kind without a factory becomes a bare entity. The kit spawns the project spawns collection at start, answers spawn.request events from triggers and scripts, keeps a cap on how many spawned entities are alive, and brings back those tagged respawn after a delay. With game.engine, a scene can carry its own spawns that come and go with it. It draws nothing: what a kind looks like belongs to the kit behind the factory.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "spawn.system" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "spawn.system",
                              config: { "respawnSeconds": 5, "maxEntities": 300 } }

It requires game.engine and provides the service spawn.entities. NPC, vehicle and prop kits register their factories with it.

First example #

<!-- test: kits/game_engine/host/tests/spawn_system.rs::the_spawns_collection_is_spawned_in_document_order --> Entries of the spawns collection are spawned in document order, so entity ids are the same on every run. An entry without a valid at is skipped:

{ "spawns": [
  { "id": "c", "kind": "crate", "at": [1, 0, 1], "tags": ["loot"] },
  { "kind": "ghost" },
  { "kind": "barrel", "at": [2, 0, 2], "yaw": 0.5, "components": { "hp": 3 } }
] }

This spawns a crate and a barrel (the ghost entry has no place). spawn.entities.count() answers 2, and spawn.entities.specOf("e1") returns the first entry.

A spawn entry #

{ kind, at, id?, yaw?, tags?, components?, archetype? } where at is a point [x, y, z], { zone } (a point inside the zone) or { entity, offset? } (next to another entity). components sets initial components, archetype names one to build from.

Settings #

SettingTypeDefaultMeaning
respawnSecondsnumber, 0 to 1203seconds before an entity tagged respawn comes back at its spawn point
maxEntitiesinteger, 1 to 2048256cap on live spawned entities; a spawn above it is skipped with a warning

Services #

  • spawn.entities (service), methods:
    • spawn(spec): spawns an entry and returns the new entity id, or null if the entry is malformed (no string kind or no at), the cap is reached, or a hook cancelled it.
    • despawn(id): removes an entity on purpose: it is not respawned.
    • respawn(id, at?): removes the entity and brings it back after the respawn delay, optionally at a new at.
    • count(): live spawned entities.
    • specOf(id): the entry that produced an entity, or null.

Events #

  • spawn.request (listened): a spawn entry as payload. A trigger can write { "emit": { "topic": "spawn.request", "payload": {...} } }; invalid payloads are ignored.
  • entity.despawned (listened): an entity this kit spawned was destroyed; if it was not removed on purpose and carries the respawn tag, it is queued to come back.
  • spawn.spawned (event): an entity was created, payload { entity, kind, tags }.
  • spawn.despawned (event): a spawned entity is gone, payload { entity, id, kind }; sent before any respawn is planned.

Hooks and modding #

A game (a hooks document) or a mod can change these decisions without touching the kit:

  • spawn.entity, payload { kind, tags }: before an entity is spawned, after the cap; kind can be changed (another factory), a cancel spawns nothing. Then the kernel hook engine.spawn is asked (archetype and tags can change, a cancel spawns nothing and uses no entity id).
  • spawn.respawn, payload { entity, kind, delay }: before a respawn is queued; delay can be changed, a cancel means it does not come back.
{ "id": "no_respawn", "hook": "spawn.respawn", "match": { "kind": "boss" }, "cancel": true }

A mod can override respawnSeconds and maxEntities and add, patch or remove documents of the spawns collection.

Scenes #

With game.engine, a scene's data.spawns (same entries) are spawned on entering the scene and despawned on leaving it, with no respawn; pending respawns of that scene are dropped. The project spawns stay global. A load rebuilds each entity's scene.

Engines #

The same Rust module runs on the web, the native player and the Godot, Unity and Unreal wrappers.

When it does not do what you expect #

  • Nothing appears: the entry has no string kind, or no valid at; or maxEntities is reached; or a hook cancelled it.
  • An entity does not come back: it needs the tag respawn and must not have been removed with despawn (that means on purpose).
  • A respawn comes late: check respawnSeconds and any spawn.respawn hook that raises delay.
  • A kind appears as an empty entity: no kit registered a factory for it; install the kit that provides it.

Limits #

  • It does not decide what a kind looks like or does; factories belong to other kits.
  • Respawn brings back the original entry at its spawn point, not the entity's old state.
  • Scene spawns never respawn.

Versions #

Kit version 1.4.0; the service spawn.entities is at 1.0.0. Since 1.4.0 an entity is created through the kernel's engine.spawn hook after spawn.entity.