Skip to content

Editor API ​

Namespace GodotECS.Editor, plus the plugin entry point (global class). Editor tooling: debugger dock, entity inspector backend, AABB gizmos, and the bake button. See Editor tools for workflows.

plugin ​

csharp
[Tool]
public partial class plugin : EditorPlugin
{
    public override void _EnterTree();
    public override void _ExitTree();
}

Compiled only with TOOLS. _EnterTree() adds an EntityDebuggerDock { Name = "ECS Debugger" } to DockSlot.LeftUr and wires the companion tools as children: a BakeTool (visible Bake section inside the dock), an EntityInspector (non-visual mapping backend) and an EcsGizmos node parented to a real 3D host — the editor 3D viewport first, then the edited scene root, then the dock as inert fallback (a Node3D under a Control would never render). _ExitTree() removes the dock and frees the gizmos. Registered via addons/godotecs/plugin.cfg (name="GodotECS", version="0.2.0", script="plugin.cs").

EntityDebuggerDock ​

csharp
public partial class EntityDebuggerDock : Control
{
    public const int TimingCapacity = 120;   // frame-timing ring buffer
    public const double HashInterval = 2.0;  // seconds between throttled hash refreshes

    public bool Paused { get; private set; }
    public bool StepPending { get; }
    public World CurrentWorld { get; private set; }
    public int TimingCount { get; }
    public int SnapshotSaveCount { get; }
    public int SystemTimingCount { get; }

    public EntityDebuggerDock();

    public void RegisterTiming(float ms);
    public void ClearTimings();
    public float AverageMs();
    public float[] GetTimingsOrdered();
    public void RegisterSystemTiming(string systemName, float ms);  // EMA 0.9·prev + 0.1·ms
    public void ClearSystemTimings();
    public System.Collections.Generic.List<(string Name, float Ms)> TopSystems(int max = 20);
    public void SetPaused(bool paused);
    public void TogglePause();
    public void RequestStep();
    public bool ConsumeStep();
    public World[] GetWorlds();
    public void SelectWorld(int index);
    public static ulong ComputeHash(World w);
    public void RefreshNow();                // includes hash
    public void RefreshNow(bool includeHash);
    public void RefreshHash();
    public override void _Ready();
    public override void _Process(double delta);
}
  • TimingCapacity — ring-buffer size (120 samples).
  • Constructor sets CurrentWorld = World.Default.
  • RegisterTiming(float ms) — pushes a frame sample; negative/NaN/infinite values are stored as 0. Redraws the graph when in the tree.
  • ClearTimings() — empties the buffer.
  • AverageMs() — mean over stored samples (0 when empty).
  • GetTimingsOrdered() — oldest-first copy of the stored samples.
  • RegisterSystemTiming(name, ms) — feeds the per-system EMA (0.9·prev + 0.1·ms); invalid input (empty name, NaN/inf/negative) is ignored. TopSystems(max = 20) returns the systems sorted by descending average ms — the dock shows the top-20 list in its own label. ClearSystemTimings() empties the table.
  • SetPaused(bool) — sets Paused and updates the pause button label ("Riprendi" / "Pausa"). TogglePause() flips it.
  • RequestStep() — arms the one-shot step flag (StepPending); ConsumeStep() returns false when disarmed, else disarms and returns true.
  • GetWorlds() — { World.Default, World.Preview }. SelectWorld(int index) — switches CurrentWorld (out-of-range ignored, keeps selection) and refreshes.
  • ComputeHash(World w) — Snapshot.Hash(w).
  • Hashing is throttled (Snapshot.Save costs a hitch): the periodic refresh every 0.5 s calls RefreshNow(includeHash: false), and a dedicated timer refreshes the hash every HashInterval (2 s) via RefreshHash(), which increments SnapshotSaveCount and updates the hash label (hash: <X16> bytes: <n>, or err <Exception>). The manual refresh (button / RefreshNow()) includes the hash. RefreshHash is safe to call without UI.
  • _Ready() — builds the UI (world selector, info label, per-system top label, graph, pause/step/refresh buttons) and refreshes. _Process(double delta) — records delta * 1000 ms when unpaused and refreshes every 0.5 s.

TimingGraph ​

Nested inside EntityDebuggerDock (sealed partial class TimingGraph : Control, constructed with its owning dock). Custom _Draw(): dark background rect, one green bar per stored sample scaled to 1.2 × max, and a yellow horizontal line at the average. No public members beyond the constructor.

EntityInspector ​

csharp
public partial class EntityInspector : Node
{
    public void RegisterMapping(int multimeshInstanceId, int instanceIndex, Entity entity);
    public bool TryGetEntity(int multimeshInstanceId, int instanceIndex, out Entity entity);
    public bool UnregisterMapping(int multimeshInstanceId, int instanceIndex);
    public void ClearMappings();
    public int MappingCount { get; }
    public Godot.Collections.Dictionary Inspect(World world, Entity entity);
    public Godot.Collections.Dictionary InspectWith<T>(World world, Entity entity)
        where T : struct, IComponentData;
}
  • Mapping key packs both ids: (long)(uint)multimeshInstanceId << 32 | (uint)instanceIndex. TryGetEntity returns false on miss; UnregisterMapping returns whether an entry was removed.
  • Inspect(World world, Entity entity) — Dictionary with world (name), tick (int), index, version, alive, liveCount. Null world → World.Default.
  • InspectWith<T>(World world, Entity entity) — Inspect plus has_<TypeName> (bool) and, when present, data_<TypeName> (ToString() of the component or err:<Exception>).

EcsGizmos ​

csharp
public partial class EcsGizmos : Node3D
{
    public bool ShowGizmos { get; set; }
    public int BoxCount { get; }
    public override void _Ready();
    public void AddBox(in GodotECS.Math.Aabb box);
    public void SetBoxes(IEnumerable<GodotECS.Math.Aabb> boxes);
    public void ClearBoxes();
    public override void _Process(double delta);
    public void Rebuild();
}
  • ShowGizmos — master toggle, default true. BoxCount — queued box count.
  • AddBox appends, SetBoxes replaces (null → empty), ClearBoxes empties; all mark the mesh dirty.
  • Rebuild() — recreates the internal ImmediateMesh/MeshInstance3D nodes if missing, clears surfaces, and emits 12 line edges per box (unshaded green, shadow-casting off). No-op other than clearing the dirty flag when empty or hidden.
  • _Process(double) — hides the mesh node when ShowGizmos is false or the node is off-tree; otherwise shows it and rebuilds only when dirty.

BakeTool ​

csharp
public partial class BakeTool : VBoxContainer
{
    public string LastStatus { get; private set; }   // "idle" | "no-root" | "baked-subtree:<n>" | "bake-error"
    public int LastBakedCount { get; private set; }
    public override void _Ready();
    public bool TryBake();
    public bool TryBake(Node root);
    public static void ClearPreview();
}
  • LastStatus — starts "idle"; mirrored to the status label. LastBakedCount — entity count of the last successful bake.
  • TryBake(Node root) — clears World.Preview (see ClearPreview), then calls Baker.BakeSubtreeInto(World.Preview, root) directly — no reflection. Success → baked-subtree:<n> with LastBakedCount = n; exception → PushWarning + bake-error + false.
  • TryBake() (no argument) returns false with status no-root: baking requires an explicit subtree.
  • ClearPreview() — static; empties World.Preview via DestroyBatch (and its orphaned dynamic buffers), so consecutive bakes never accumulate leftovers.
  • The old reflection layer (BakeAll / Bake / BakeWorld / BakeActiveScene probing, statuses baker-missing / baker-no-entrypoint / baked:<name> / bake-failed:<name>) was removed in v0.2 — the authoring assembly is a hard dependency now.

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