Authoring workflow
Design levels with normal Godot nodes, convert them to ECS data at bake time, and spawn that data at runtime. The scene tree stays the level-design surface; the ECS World is the runtime simulation.
Concepts
- Authoring node (
EcsAuthoring3D/EcsAuthoring2D): a regularNode3D/Node2Dyou place in the editor with exported fields (mesh, health, velocity, …). It never simulates — it describes one entity. - Bake: converting an authoring subtree into an
EntitySceneresource (a serialized snapshot of entities + bake metadata). Baking is a cold path: allocations are allowed, and it runs in the editor or at scene setup, never in a hot loop. EntityScene: a GodotResourceholding the baked bytes plusEntityCount,ComponentNames, and source keys for remapping internal entity references.Instantiateappends to a world without clearing it.- Spawning: creating live entities from an
EntitySceneat runtime, optionally scattered or distance-gated viaEntitySceneInstance.
EcsAuthoring3D fields
GodotECS.Authoring.EcsAuthoring3D, extends Node3D. Every field is an exported property editable in the inspector:
| Field | Type | Default | Baked into |
|---|---|---|---|
Mesh | Mesh | null | mesh group registration at sync time (RenderMeshRef, group resolved by MeshGroupManager.Register, stays 0 at bake) |
MaterialOverride | Material | null | material used when the mesh group registers |
IsStatic | bool | false | true → PhysicsStaticTag + PhysicsRadius; false → PhysicsVelocity + PhysicsRadius |
Radius | float | 0.5 | PhysicsRadius (clamped to 0.5 if ≤ 0.01) |
Health | int | 100 | Health.Value |
MaxHealth | int | 100 | Health.Max (raised to Health if lower) |
Velocity | Vector3 | zero | Velocity.Value and PhysicsVelocity.Value (dynamic only) |
Tint | Color | white | RenderColor |
Every baked entity also gets LocalTransform (from GlobalTransform: position, rotation quaternion, uniform scale averaged from the basis), PhysicsLayer.Default, and a Parent component when the authoring node is nested under another baked entity.
Useful members: Entity Bake(World world) bakes just this node into a live world; EntityScene BakeScene() bakes it into a throwaway world and returns a standalone scene, cached on the Baked property.
EcsAuthoring2D fields
GodotECS.Authoring.EcsAuthoring2D, extends Node2D:
| Field | Type | Default | Baked into |
|---|---|---|---|
Texture | Texture2D | null | atlas group registration at sync time (SpriteRef, group stays 0 at bake) |
Size | Vector2 | (16, 16) | SpriteSize |
Modulate | Color | white | RenderColor |
Health | int | 100 | Health (Value and Max) |
Velocity2D | Vector2 | zero | Velocity.Value as Vector3(x, y, 0) |
LocalTransform comes from GlobalPosition/GlobalRotation (Z-axis quaternion) and averaged GlobalScale. Same Bake / BakeScene / Baked members as the 3D variant.
Baker flow
GodotECS.Authoring.Baker is a static class with two entry points:
EntityScene scene = Baker.BakeSubtree(rootNode); // bake to a new EntityScene
int count = Baker.BakeSubtreeInto(world, rootNode); // bake into an existing world, returns entity countBehaviour:
- Pre-order traversal — each parent is baked before its children so the child's
Parentcomponent can reference the already-created parent entity. - Only
EcsAuthoring3D/EcsAuthoring2Dnodes produce entities. PlainNode3D/Nodecontainers produce nothing but their children are still visited, with the nearest baked ancestor as parent. BakeSubtreeworks on a throwawayWorldand returnsEntityScene.FromWorld(tmp);BakeSubtreeIntowrites directly into your world (e.g.World.Previewfor editor ghosts) and returns the baked count.- Both throw
ArgumentNullExceptionon a null root (or null world). No reflection is used; dispatch is byischecks.
Typical editor usage: select the level root, press the Bake ECS button (see Editor tools), save the resulting EntityScene as a .tres prefab.
Spawning with EntitySceneInstance
GodotECS.Authoring.EntitySceneInstance, extends Node. Exports:
| Field | Type | Default | Meaning |
|---|---|---|---|
Scene | EntityScene | null | baked scene to spawn |
Count | int | 1 | how many copies SpawnAll creates (negative → 0) |
SpawnRadius | float | 5.0 | ring radius used to scatter copies when Count > 1 |
StreamDistance | float | 100.0 | if origin.Length() > StreamDistance (and > 0), nothing spawns — cheap distance streaming |
// One entity at a position in the default world.
Entity root = instance.SpawnDefault(new Vector3(10, 0, 5));
// One entity in an explicit world.
Entity root2 = instance.Spawn(World.Default, new Vector3(10, 0, 5));
// Count copies scattered on a ring around origin.
Entity[] roots = instance.SpawnAll(World.Default, Vector3.Zero);Spawn returns Entity.Null when the scene is null/empty. SpawnAll returns an array of Entity.Null in the same cases, and when the origin is beyond StreamDistance. When scattering, copy i is offset by angle (i / n) * TAU on the XZ plane — deterministic, no RNG involved.
StaticBake sources
GodotECS.Authoring.StaticBake.BakeFromStatics(Node root) walks a Godot subtree and extracts world-space static colliders for the arcade-physics solver. Returns StaticCollider[] (empty array for a null root). Sources:
CollisionShape3Dunder aStaticBody3D—BoxShape3D→ box (half extents scaled by the shape's global scale),SphereShape3D→ sphere (radius scaled by max axis),CapsuleShape3D→ capsule (radius from XZ scale, height from Y scale). Shapes with noStaticBody3Dancestor, disabled shapes, and shapes with the_baked_disabledmeta flag are skipped. The body'sCollisionLayeris copied to the collider.GridMap— every used cell becomes a box centered on the cell's world position withCellSize * 0.5half extents, using the grid'sCollisionLayer.TileMap— every used cell of layer0becomes a flat box (TileSize * 0.5in X/Y,0.5in Z, scaled by the TileMap scale) with layer1. Requires aTileSetand at least one layer.
Rebake (or flag dirty) whenever a designer moves a wall — the solver never moves these colliders itself. Feed the result into the physics StaticRegistry singleton at scene setup.
Next steps
- Godot bridge — how baked entities talk back to Godot at runtime.
- Authoring API — exact signatures for
Baker, both authoring nodes,EntitySceneInstance, andStaticBake.