Scene Tree Initialization and Timing
The godot-bevy library automatically parses the Godot scene tree and creates corresponding Bevy entities before your game logic runs. This means you can safely query for scene entities in your Startup
systems:
#![allow(unused)] fn main() { #[bevy_app] fn build_app(app: &mut App) { app.add_systems(Startup, find_player); } fn find_player(query: Query<&PlayerBundle>) { // Your player entity will be here! ✨ for player in &query { println!("Found the player!"); } } }
How It Works
The scene tree initialization happens in the PreStartup
schedule, ensuring entities are ready before any Startup
systems run. This process has two parallel systems:
initialize_scene_tree
- Traverses the entire Godot scene tree and creates Bevy entities with components likeGodotNodeHandle
,Name
, transforms, and moreconnect_scene_tree
- Sets up event listeners for runtime scene changes (nodes being added, removed, or renamed)
Both systems run in parallel during PreStartup
, and both complete before your Startup
systems run. This means you can safely query for Godot scene entities in Startup
!
Runtime Scene Updates
After the initial parse, the library continues to listen for scene tree changes during runtime. This is handled by two systems that run in the First
schedule:
write_scene_tree_events
- Receives events from Godot (via an mpsc channel) and writes them to Bevy's event systemread_scene_tree_events
- Processes those events to create/update/remove entities
This separation allows other systems to also react to SceneTreeEvent
s if needed.
What Components Are Available?
When the scene tree is parsed, each Godot node becomes a Bevy entity with these components:
GodotNodeHandle
- Reference to the Godot nodeName
- The node's name from GodotTransform2D
orTransform3D
- For Node2D and Node3D types respectivelyGroups
- The node's group membershipsCollisions
- If the node has collision signals- Node type markers - Components like
ButtonMarker
,Sprite2DMarker
, etc. - Custom bundles - Components from
#[derive(BevyBundle)]
are automatically added
BevyBundle Component Timing
If you've defined custom Godot node types with #[derive(BevyBundle)]
, their components are added immediately during scene tree processing. This happens:
- During
PreStartup
for nodes that exist when the scene is first loaded - During
First
for nodes added dynamically at runtime
This means BevyBundle components are available in Startup
systems for initial scene nodes, and immediately available for dynamically added nodes.
#![allow(unused)] fn main() { #[derive(GodotClass, BevyBundle)] #[class(base=Node2D)] #[bevy_bundle((Health), (Velocity))] pub struct Player { base: Base<Node2D>, } // This will work in Startup - the Health and Velocity components // are automatically added during PreStartup for existing nodes fn setup_player(mut query: Query<(Entity, &Health, &Velocity)>) { for (entity, health, velocity) in &mut query { // Player components are guaranteed to be here! } } }
Best Practices
- Use
Startup
for initialization - Scene entities are guaranteed to be ready - Use
Update
for gameplay logic - This is where most of your game code should live - Custom
PreStartup
systems - If you add systems toPreStartup
, be aware they run before scene parsing unless you explicitly order them with.after()
Understanding the Event Flow
Here's what happens when a node is added to the scene tree during runtime:
- Godot emits a
node_added
signal - The
SceneTreeWatcher
(on the Godot side) receives the signal - It sends a
SceneTreeEvent
through an mpsc channel write_scene_tree_events
(inFirst
schedule) reads from the channel and writes to Bevy's event systemread_scene_tree_events
(also inFirst
schedule) processes the event and creates/updates entities
This architecture allows for flexible event handling while maintaining a clean separation between Godot and Bevy.