Skip to content

Godot bridge ​

The bridge is the controlled boundary between ECS simulation and Godot. ECS systems produce data or requests; bridge nodes consume them at main-thread sync points.

Sync-point rule ​

Never access Godot objects from ECS jobs or hot per-entity loops. Audio, navigation, signals and other engine-facing operations happen through the bridge on the main thread.

Per-frame budgets are explicit: AudioBridge.MaxVoices is 32 and its pending queue is capped at MaxPending = 1024; NavBridge.PathsPerFrame defaults to 64. Overflow is deliberately bounded rather than allowing unbounded backlog.

Signals ​

EcsSignals batches EntityDied(index, version) and HitPlayer(damage).

csharp
signals.EnqueueDeath(deadEntity);
signals.EnqueueHit(25);
int emitted = signals.EmitPending();

The two internal queues are thread-safe and each is capped at MaxPendingPerQueue = 4096. If a queue is full, the oldest entry is dropped. Entity.Null deaths are ignored. EmitPending() drains deaths before hits; _Process() drains automatically.

Audio ​

csharp
audio.RegisterBank(0, stream);
audio.PushBank(0, muzzlePos, 0.8f);
int played = audio.Flush();

MaxVoices = 32 limits each flush. The pending queue is capped at 1024. When the queue is full, a new event replaces the quietest pending event only if it is louder; otherwise the new event is dropped. This preserves strong events during bursts.

Flush() reuses its scratch list and skips sorting when there are at most 32 pending events. Above 32 it keeps the loudest 32. If Sink is non-null, it is the only playback destination; otherwise the real AudioStreamPlayer3D pool is used. This makes injected test sinks exclusive rather than double-playing with real players.

NavBridge resolves pending NavRequest components into NavPath buffers, up to Budget requests per physics frame. Failed queries leave requests pending without consuming budget.

The bridge caches an EntityQuery per World in a ConditionalWeakTable. It therefore scans only chunks containing NavRequest, rather than copying and testing every live entity each physics frame. The cache is weakly keyed so it does not retain disposed worlds.

csharp
int done = NavBridge.ProcessBudget(World.Default, query, 64);

The default StraightLineQuery requires no navigation server. Production integrations can inject an INavQuery implementation.

GDScript facade ​

GdScriptApi exposes World.Default to GDScript. SpawnScene returns a Vector2I(Index, Version) key; KillEntity(index, version) defers destruction through an ECB and flushes automatically. Always keep the index/version pair together to avoid ABA errors after index reuse.

gdscript
var key = ecs.SpawnScene(enemy_scene, Vector3(10, 0, 5))
var pos = ecs.GetEntityPos(key.x, key.y)
ecs.KillEntity(key.x, key.y)

GetPhysicsHz() and GetLodBias() expose the core QualityConfig singleton. GetEntityPos returns Vector3.ZERO for a dead/stale entity or one without LocalTransform.

Full API ​

See Bridge API for exact signatures.

GodotECS docs — version 0.2.0. All rights reserved until a license is added.