Migration Guide: v0.8 to v0.9

This guide covers breaking changes and new features when upgrading from godot-bevy 0.8.x to 0.9.0.

Table of Contents

Godot Bevy now uses standard Bevy Transforms (Breaking Change)

What Changed

In v.0.9.0, we've made significant changes to how we use bevy Transform components. We now operate directly on standard Transform components and you can too, whereas before, we had wrapped the Transform component in higher level Transform2D and Transform3D components and required you to use the wrappers. While wrapping provided important benefits (change detection, dual-godot/bevy-API access with built-in multi-threaded safety) it came with some notable drawbacks (incompatible with other bevy ecosystem plugins that operate directly on Transforms, extra memory overhead, less ergonomic as it required extra API calls to access the underlying data).

Migration Path

The main change is switching all of your usages of godot_bevy::prelude::Transform2D or godot_bevy::prelude::Transform3D to bevy::transform::components::Transform.

Before (v.0.9.0)

#![allow(unused)]
fn main() {
fn orbit_system(
    // The `transform` parameter is a Bevy `Query` that matches all `Transform2D` components.
    // `Transform2D` is a Godot-Bevy-provided component that matches all Node2Ds in the scene.
    // (https://docs.rs/godot-bevy/latest/godot_bevy/plugins/core/transforms/struct.Transform2D.html)
    mut transform: Query<(&mut Transform2D, &InitialPosition, &mut Orbiter)>,

    // This is equivalent to Godot's `_process` `delta: float` parameter.
    process_delta: Res<Time>,
) {
    // For single matches, you can use `single_mut()` instead:
    // `if let Ok(mut transform) = transform.single_mut() {`
    for (mut transform, initial_position, mut orbiter) in transform.iter_mut() {
        transform.as_godot_mut().origin =
            initial_position.pos + Vector2::from_angle(orbiter.angle) * 100.0;
        orbiter.angle += process_delta.as_ref().delta_secs();
        orbiter.angle %= 2.0 * PI;
    }
}
}

After (v.0.9.0)

#![allow(unused)]
fn main() {
fn orbit_system(
    // The `transform` parameter is a Bevy `Query` that matches all `Transform` components.
    // `Transform` is a Godot-Bevy-provided component that matches all Node2Ds in the scene.
    // (https://docs.rs/godot-bevy/latest/godot_bevy/plugins/core/transforms/struct.Transform.html)
    mut transform: Query<(&mut Transform, &InitialPosition, &mut Orbiter)>,

    // This is equivalent to Godot's `_process` `delta: float` parameter.
    process_delta: Res<Time>,
) {
    // For single matches, you can use `single_mut()` instead:
    // `if let Ok(mut transform) = transform.single_mut() {`
    for (mut transform, initial_position, mut orbiter) in transform.iter_mut() {
        let position2d = initial_position.pos + Vector2::from_angle(orbiter.angle) * 100.0;
        transform.translation.x = position2d.x;
        transform.translation.y = position2d.y;
        orbiter.angle += process_delta.as_ref().delta_secs();
        orbiter.angle %= 2.0 * PI;
    }
}
}

Breaking changes

  • godot_bevy::prelude::Transform2D and godot_bevy::prelude::Transform3D were removed

Migration Checklist

  • Transform components changed: Replaced godot_bevy::prelude::Transform2D and godot_bevy::prelude::Transform3D with bevy::transform::components::Transform. The APIs from the former must be mapped to the latter:
    • Remove the now extra as_bevy() and as_bevy_mut() calls, since you're operating directly on bevy Transforms, e.g., transform.as_bevy_mut().translation.x -> transform.translation.x. These changes should be easy.
    • Remap the as_godot() and as_godot_mut() calls. These changes may be tricky, as there may not be direct replacements for all Godot APIs in native Bevy transforms. One important benefit of doing this work is that it promotes a clean separation where your bevy transform systems remain portable to other Bevy projects (with or without godot-bevy). You can always fall back on using GodotNodeHandle to get at the original Godot Node APIs, then replicate position, scale, and rotation back to the bevy Transform as necessary.

Assets Plugin Moved to Optional (Breaking Change)

What Changed

In v0.9.0, GodotAssetsPlugin has been moved from GodotCorePlugins (included by default) to GodotDefaultPlugins (optional). This change provides a cleaner architecture where core functionality is truly minimal and reduces runtime overhead for applications that don't need to load Godot resources through Bevy's asset system.

Who Is Affected

You are affected if:

  • You use GodotCorePlugins directly (without GodotDefaultPlugins)
  • You load Godot resources using Handle<GodotResource> or AssetServer
  • You use GodotAudioPlugin or GodotPackedScenePlugin (they require assets)

Migration Path

If you use GodotDefaultPlugins

No changes needed - GodotAssetsPlugin is included in GodotDefaultPlugins.

#![allow(unused)]
fn main() {
#[bevy_app]
fn build_app(app: &mut App) {
    app.add_plugins(GodotDefaultPlugins); // ✅ Assets included
}
}

If you use GodotCorePlugins and need asset loading

Add GodotAssetsPlugin explicitly:

#![allow(unused)]
fn main() {
#[bevy_app]
fn build_app(app: &mut App) {
    // GodotCorePlugins no longer includes assets
    app.add_plugins(GodotAssetsPlugin)      // Add this line
       .add_plugins(GodotAudioPlugin)       // Requires GodotAssetsPlugin
       .add_plugins(GodotPackedScenePlugin); // Requires GodotAssetsPlugin
}
}

If you don't need asset loading

No changes needed - enjoy reduced runtime overhead!

#![allow(unused)]
fn main() {
#[bevy_app]
fn build_app(app: &mut App) {
    // Now truly minimal - no asset loading overhead
    app.add_plugins(GodotTransformSyncPlugin)
       .add_plugins(GodotSignalsPlugin)
       .add_plugins(BevyInputBridgePlugin);
}
}

Breaking Changes

  • GodotCorePlugins no longer includes GodotAssetsPlugin
  • GodotAssetsPlugin is now in GodotDefaultPlugins
  • GodotAudioPlugin and GodotPackedScenePlugin require GodotAssetsPlugin to function

Migration Checklist

  • Using GodotDefaultPlugins: No action needed
  • Using GodotCorePlugins + asset loading: Add app.add_plugins(GodotAssetsPlugin)
  • Using GodotAudioPlugin: Ensure GodotAssetsPlugin is included
  • Using GodotPackedScenePlugin: Ensure GodotAssetsPlugin is included
  • Pure ECS without assets: Consider removing unused plugins for better runtime performance

Gamepad Support Now Optional

What Changed

In v0.9.0, gamepad support through Bevy's GilrsPlugin is now controlled by an optional feature flag bevy_gamepad. This feature is enabled by default but can be disabled to reduce compile time and dependencies for applications that don't use gamepads.

Migration Path

If you use gamepads with Bevy's input API

No changes needed if using GodotDefaultPlugins - GilrsPlugin is included automatically.

If using custom plugin setup: Add GilrsPlugin manually:

#![allow(unused)]
fn main() {
use godot_bevy::prelude::*; // Includes bevy_prelude::GilrsPlugin

#[bevy_app]
fn build_app(app: &mut App) {
    app.add_plugins(GilrsPlugin)  // Available via bevy_prelude
       .add_plugins(BevyInputBridgePlugin);
}
}

If you only use Godot's gamepad input

No changes needed - Godot's gamepad support through GodotInputEventPlugin works regardless of the feature flag.

If you don't use gamepads at all

Optional: Disable the feature for faster compile times:

[dependencies]
godot-bevy = { version = "0.9", default-features = false, features = [...] }

What Still Works Without the Feature

  • ✅ Godot's gamepad input via GodotInputEventPlugin
  • ✅ Raw gamepad events in EventReader<GamepadButtonInput> and EventReader<GamepadAxisInput>
  • ✅ All keyboard, mouse, and touch input

What Requires the Feature

  • ❌ Bevy's standard gamepad API (ButtonInput<GamepadButton>, Axis<GamepadAxis>)
  • GilrsPlugin functionality
  • ❌ Cross-platform gamepad detection outside of Godot

Note: GilrsPlugin is included in GodotDefaultPlugins when the feature is enabled, but must be added manually if using a custom plugin configuration.

Scene Tree Plugin Configuration Simplified

What Changed

The add_transforms configuration option has been removed from GodotSceneTreePlugin. Transform components are now automatically added to scene tree entities when the GodotTransformSyncPlugin is included in your app.

Migration Path

If you were using the add_transforms configuration option, you can simply remove it. Transform components will be automatically added if you include the transform plugin.

Before (v0.8.x)

#![allow(unused)]
fn main() {
app.add_plugins(GodotSceneTreePlugin {
    add_transforms: true,
    add_child_relationship: true,
});
}

After (v0.9.0)

#![allow(unused)]
fn main() {
// Transform components are automatically added when GodotTransformSyncPlugin is included
app.add_plugins(GodotSceneTreePlugin {
    add_child_relationship: true,
});

// Add the transform plugin to get automatic transform components
app.add_plugins(GodotTransformSyncPlugin::default());
}