Migration Guide: v0.9 to v0.10
This guide covers breaking changes and new features when upgrading from godot-bevy 0.9.x to 0.10.0.
Table of Contents
Breaking changes:
- Upgrade to Bevy 0.17
- Custom scene tree relationships
- NodeTreeView::from_node now returns a Result type
connect_mapCallback Now ReturnsOption<T>
Breaking: Upgrade to Bevy 0.17
✨ Godot-Bevy is now up to date with Bevy 0.17! ✨
Here is the Bevy 0.16 to 0.17 migration guide for reference: https://bevy.org/learn/migration-guides/0-16-to-0-17/
The biggest difference is that Godot-Bevy now uses Message instead of Event.
Migration Path
Cargo.toml
🗑️ Before:
bevy = { version = "0.16", default-features = false, features = ["bevy_state"] }
bevy_asset_loader = "0.23.0"
🟢 After:
bevy = { version = "0.17", default-features = false, features = ["bevy_state"] }
bevy_asset_loader = "0.24.0-rc.1"
Event to Message
🗑️ Before:
#[derive(Event, Debug)]
pub enum SceneOperationEvent {
🟢 After:
#[derive(Message, Debug)]
pub enum SceneOperationMessage {
🗑️ Before:
app.add_event::<SceneOperationEvent>()
🟢 After:
app.add_message::<SceneOperationMessage>()
🗑️ Before:
mut operation_events: EventReader<SceneOperationEvent>,
🟢 After:
mut operation_events: MessageReader<SceneOperationMessage>,
Migration Checklist
-
Update Bevy to
0.17. -
Update Bevy Asset Loader to
0.24.0-rc.1. -
Replace usages of
EventwithMessage.
Breaking: Custom Scene Tree Relationships
Godot's scene tree is now represented by a custom ECS relationship:
GodotChildOf / GodotChildren. The built-in Bevy ChildOf / Children
relationship is no longer used for scene tree mirroring.
This avoids conflicts with other plugins that use Bevy's hierarchy for their own purposes (physics, AI, scene graphs, etc.).
Migration Path
Queries and Traversal
🗑️ Before:
fn parent_of(entity: Entity, query: Query<&ChildOf>) -> Option<Entity> {
query.get(entity).ok().map(|parent| parent.parent())
}
fn children_of(entity: Entity, query: Query<&Children>) -> Vec<Entity> {
query
.get(entity)
.map(|children| children.iter().copied().collect())
.unwrap_or_default()
}
🟢 After:
fn parent_of(entity: Entity, query: Query<&GodotChildOf>) -> Option<Entity> {
query.get(entity).ok().map(|parent| parent.get())
}
fn children_of(entity: Entity, query: Query<&GodotChildren>) -> Vec<Entity> {
query
.get(entity)
.map(|children| children.iter().copied().collect())
.unwrap_or_default()
}
Configuration
The scene_tree_add_child_relationship attribute and
GodotSceneTreePlugin { add_child_relationship: ... } have been removed.
If you want children to outlive their parents, use the new
scene_tree_auto_despawn_children attribute (or the plugin config).
🗑️ Before:
#[bevy_app(scene_tree_add_child_relationship = false)]
fn build_app(app: &mut App) {}
app.add_plugins(GodotSceneTreePlugin {
add_child_relationship: true,
});
🟢 After:
#[bevy_app(scene_tree_auto_despawn_children = false)]
fn build_app(app: &mut App) {}
app.add_plugins(GodotSceneTreePlugin {
auto_despawn_children: true,
});
Breaking changes
- Bevy
ChildOf/Childrenare no longer used for Godot scene tree hierarchy. scene_tree_add_child_relationshipwas removed.- New
scene_tree_auto_despawn_childrenconfiguration option.
Migration Checklist
-
Replace
ChildOf/Childrenqueries withGodotChildOf/GodotChildren. -
Remove
scene_tree_add_child_relationshipusage. -
Use
scene_tree_auto_despawn_childrenif you need to keep children alive on parent despawn.
Breaking: NodeTreeView::from_node now returns a Result type
NodeTreeView::from_node derive macro now returns a Result<Self, NodeTreeViewError> type to avoid surprise panic in
user code if a godot node is not found.
Migration Path
Use match, if let or unwrap to handle the Result type returned by NodeTreeView::from_node.
Example 1:
🗑️ Before:
let mut mob_nodes = MobNodes::from_node(mob);
🟢 After:
let mut mob_nodes = MobNodes::from_node(mob).unwrap();
Example 2:
🗑️ Before:
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| MenuUi::from_node(root))) {
Ok(menu_ui) => {
info!("MainMenu: Successfully found menu nodes");
}
Err(_) => {
debug!("MainMenu: Menu nodes not ready yet, will retry next frame");
}
}
🟢 After:
match MenuUi::from_node(root) {
Ok(menu_ui) => {
info!("MainMenu: Successfully found menu nodes");
}
Err(_) => {
debug!("MainMenu: Menu nodes not ready yet, will retry next frame");
}
}
Breaking changes
Return type of NodeTreeView::from_node derive macro changed from Self to Result<Self, NodeTreeViewError>.
Migration Checklist
-
Use
match,if letorunwrapto handle theResulttype returned byNodeTreeView::from_node.
Breaking: connect_map Callback Now Returns Option<T>
The callback provided to TypedGodotSignals::connect_map (and related APIs)
must now return Option<T> instead of T. When an event is emitted,
return Some(event) or return None to suppress event emission. This applies
to both immediate (connect_map) and deferred
(TypedDeferredSignalConnections) signal connections.
This enables advanced uses like filtering certain signal events, but requires
code update for all users of connect_map and deferred connection mappers.
Migration Path
🗑️ Before (old API: return event value directly):
typed.connect_map(&mut node, "signal", None, |_args, _node_id, _ent| MyEvent {});
// or for deferred:
TypedDeferredSignalConnections::<MyEvent>::with_connection("signal", |_a, _node_id, _e| MyEvent {});
🟢 After (new API: return event value inside Some, or None to suppress):
typed.connect_map(&mut node, "signal", None, |_args, _node_id, _ent| Some(MyEvent {}));
// or for deferred:
TypedDeferredSignalConnections::<MyEvent>::with_connection("signal", |_a, _node_id, _e| Some(MyEvent {}));
Migration Checklist
-
Update all usages of
TypedGodotSignals<...>::connect_mapto returnSome(event)(orNoneto suppress event). -
Update all
TypedDeferredSignalConnections::<...>::with_connectionmappers for typed deferred signals to returnOption<T>.