Debugging
Godot-bevy's entity inspector shows Bevy entities, component values and scene-tree relationships in the Godot editor while your game runs.
Entity inspector
The Entities pane is the map of the running ECS world; Godot's Inspector shows
and edits the selected entity's components in a Bevy section below the node properties.
Node-backed entities contain their pure-ECS children through GodotChildOf. The pane hides
unnamed internal root entities by default, shows the hidden count, and has a toggle to reveal
them. Search matches names, decimal entity IDs, component type paths or an exact runtime node
path; matching entities keep their ancestors visible.
Selecting a node-backed entity selects it in Godot's Remote tree, where native node properties remain editable. Remote selection also selects the entity in the pane. This adapter uses version-specific editor widgets on 4.2–4.6; missing widgets show “not available”. Pure-ECS entities use the same Bevy Inspector through a proxy object. Choose the running instance with the session selector; the newest started session is the default.
The native Bevy section for node-backed entities requires Godot 4.4 or later. Godot 4.2
and 4.3 retain the previous component rows and identify that fallback in the section header;
the Entities pane remains available. The native section has one Bevy heading. Scalars and
tuples with one scalar field appear as one row named for the component, such as Speed. Components
with named fields or several values have foldable groups. Full Rust types are in tooltips;
generic labels preserve their arguments, such as PreviousState<GameState>, and expand to
full paths when short names collide. Runtime fields have no default-value revert.
Nested fields use Inspector sections; after three levels, field labels
show the remaining path as a breadcrumb with a copy action. Collection sizes are fixed,
and maps, sets, unsupported values and read-only fields retain their explanations.
Enabling the inspector
GodotDefaultPlugins includes the inspector. To add it individually:
#![allow(unused)] fn main() { #[bevy_app] fn build_app(app: &mut App) { app.add_plugins(GodotDebuggerPlugin); } }
Registering components
Enable automatic reflection registration on your Bevy dependency:
bevy = { version = "0.20", default-features = false, features = ["reflect_auto_register"] }
Derive Reflect on components you want to inspect. Add #[reflect(Component)]
to allow the runtime inspection service to edit their supported leaves:
#![allow(unused)] fn main() { use bevy::prelude::*; #[derive(Component, Reflect)] #[reflect(Component)] pub struct Speed(pub f32); }
The platformer example uses this automatic path for Speed, JumpVelocity,
Gravity and Player. Unregistered components display their unsupported reason.
For web and static builds, keep explicit registration as a fallback when automatic registration has not been verified with your build and loader:
#![allow(unused)] fn main() { app.register_type::<Speed>(); }
Registration also includes reflected dependencies. Spawning a component or exporting
a Godot property does not register its type. Unregistered components remain listed;
#[reflect(ignore)] fields are hidden. Components without ReflectComponent can
still be read when registered, but cannot be edited generically.
#[reflect(@InspectorReadOnly)] shows a field without allowing edits;
#[reflect(@InspectorRange::new(min, max))] bounds scalar edits (both are in the prelude).
States
The States view shares the pane's session selector and search. Derive Reflect
on your state and add Bevy's registration line:
#![allow(unused)] fn main() { app.register_type_mutable_state::<GameState>(); }
Use register_type_state::<S>() for read-only or computed states. Registration
does not initialize a state. Godot-bevy depends on bevy_state, but adding
StatesPlugin and initializing states remain the game's choice.
The Inspector separates Current, the game's Queued target, and Request.
For mutable registrations, Request offers fieldless enum variants and writes only
NextState<S> at the next First drain. It refuses to overwrite a queued transition
and reports “Already current” for the current value. Absent states stay listed;
a read-only registration without queue reflection displays “Unavailable”.
Receipts sample State<S> and NextState<S> when the Inspector reads a state:
“Queued: X; current: Y”, “Request replaced by Y”, or “No longer pending; transition
to X not observed”. “Transition to X observed” means a sample saw X as current;
it does not establish that the request caused it, and a later departure keeps
that receipt. A transition that enters and leaves the requested state between
two samples is not seen, so the receipt will say it was not observed.
Requesting a transition is not a gameplay action. For example, entering the
platformer's InGame state alone does not load a level; its Start handler also
requests level loading.
Editing and refresh
Expand a component in the Inspector to edit supported scalar leaves or select a unit enum variant. An edit displays the runtime's accepted value or an inline rejection reason; rejected edits leave the accepted value unchanged. Wide integers use exact decimal text. Entity and node references are navigation links. Game systems can overwrite accepted edits on later ticks.
Text and exact decimal integers commit on Enter; Escape restores the accepted value. Submitting an edit disables its field and shows “Waiting for acknowledgement…”. The runtime response supplies the accepted value; a rejection shows its reason beside a warning icon. Value refreshes retain folds and unfinished input. Component shape or permission changes rebuild the property rows while the proxy retains edit state and section folds. The embedded section has its own property selection; the outer Inspector's property filter does not filter its rows. Runtime edits do not enter the scene's undo history.
The pane subscribes only while visible, starting with a snapshot and applying later additions,
removals, renames, reparentings and component membership changes without rebuilding the tree.
Only the inspected entity's values are fetched at the subscription interval.
DebuggerConfig.update_interval supplies the default (0.5 seconds); configure it before
connecting the editor:
#![allow(unused)] fn main() { fn configure_debugger(mut config: ResMut<DebuggerConfig>) { config.enabled = cfg!(debug_assertions); config.update_interval = 1.0; } }
DebuggerConfig::value_limits bounds collection elements, nesting depth, and the total number
of values in one inspection. The total budget (max_values) defaults to 16,384, including
containers and read-limit notices; zero uses one so a single leaf can still be read.
Omitted collection entries appear as "N more (read limit)". A struct, tuple, or enum whose
fields cannot fit appears as a non-editable "maximum value count reached" notice.
There is no unsolicited legacy entity stream.
DebuggerConfig::snapshot_chunk_size limits the initial snapshot to 256 entity summaries per
frame by default (zero uses one), sending one chunk per First; the pane shows a loading count
and keeps its last complete tree until the final chunk arrives.
The snapshot describes the world at subscription time; changes during loading follow in the
next delta, after the configured update interval.
Selections for entities still loading are retried after the final chunk. If the runtime
disables the debugger or a chunk arrives out of sequence, the pane discards the unfinished
snapshot and keeps its last complete tree; reopen the pane to subscribe again once the
debugger is enabled.
The runtime announces godot.ready once its debugger endpoint is registered. The editor then
reads the debugger config and subscribes while the Entities pane is visible. If the readiness
message is lost, it retries the config request every 500 ms for up to 20 seconds and reports
an unanswered endpoint in the pane. Entity summaries remain subscription-only.
Inspecting live properties
The remote Inspector authors Godot properties; the Bevy inspector authors ECS fields.
Live property sync applies its usual
read/write and conflict rules. Internal snapshots are not registered for reflection.
Inspector edits run in First, after SceneTreeSet::Apply and TimeSystems.
A conflicting edit to a sync = two_way Godot property loses to the Bevy edit,
which publishes once in PostUpdate without echoing on the next frame.
Inspector refresh intervals can delay display without changing synchronization timing.
Limitations
Arbitrary not-yet-applied Bevy despawns cannot be observed through the public command queue, which holds private erased commands without target metadata (Bevy command queue source). Wide integers are edited as decimal text because a spin box value is a double and cannot represent every integer exactly.