Migration Guide: v0.7 to v0.8

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

Table of Contents

Opt-in Plugin System (Breaking Change)

What Changed

In v0.8.0, godot-bevy has adopted Bevy's philosophy of opt-in plugins. This gives users granular control over which features are included in their build.

Breaking Change: GodotPlugin now only includes minimal core functionality by default (basic scene tree access and assets). Automatic entity creation and other features must be explicitly opted-in.

Migration Path

The quickest migration is to use GodotDefaultPlugins for the old behavior, but we recommend adding only the plugins you need.

Option 1: Quick Migration (Old Behavior)

Replace the #[bevy_app] macro usage with explicit plugin registration:

Before (v0.7.x):

#![allow(unused)]
fn main() {
#[bevy_app]
fn build_app(app: &mut App) {
    // GodotPlugin automatically included all features:
    // - Automatic entity creation for scene tree nodes
    // - Transform synchronization
    // - Collision detection
    // - Signal handling
    // - Input events
    // - Audio system
    app.add_systems(Update, my_game_systems);
}
}

After (v0.8.0) - Quick Fix:

#![allow(unused)]
fn main() {
#[bevy_app]
fn build_app(app: &mut App) {
    // Add all features like before
    app.add_plugins(GodotDefaultPlugins);
    app.add_systems(Update, my_game_systems);
}
}

Pure ECS game (transforms + basic features):

#![allow(unused)]
fn main() {
#[bevy_app]
fn build_app(app: &mut App) {
    app.add_plugins(GodotSceneTreeMirroringPlugin {
            add_transforms: true,
        })
        .add_plugins(GodotTransformSyncPlugin::default())  // OneWay sync
        .add_plugins(GodotAudioPlugin);                    // Audio system
    app.add_systems(Update, my_game_systems);
}
}

Platformer (no transform conflicts):

#![allow(unused)]
fn main() {
#[bevy_app]
fn build_app(app: &mut App) {
    app.add_plugins(GodotSceneTreeMirroringPlugin {
            add_transforms: false,  // Use Godot physics instead
        })
        .add_plugins(GodotCollisionsPlugin)         // Collision detection
        .add_plugins(GodotAudioPlugin)              // Audio system
        .add_plugins(GodotSignalsPlugin);           // UI signals
    app.add_systems(Update, my_game_systems);
}
}

Full-featured game:

#![allow(unused)]
fn main() {
#[bevy_app]
fn build_app(app: &mut App) {
    app.add_plugins(GodotDefaultPlugins);   // Everything
    app.add_systems(Update, my_game_systems);
}
}

Available Plugins

Core (Always Included):

  • GodotCorePlugins - Basic scene tree access, assets, basic setup (automatically included by #[bevy_app])

Scene Tree Plugins:

  • GodotSceneTreeRefPlugin - Basic scene tree access (included in GodotCorePlugins)
  • GodotSceneTreeEventsPlugin - Monitor scene tree changes without creating entities
  • GodotSceneTreeMirroringPlugin - Auto-create entities for scene nodes (equivalent to v0.7.x behavior)

Optional Feature Plugins:

  • GodotTransformSyncPlugin - Add if you want to move/position nodes from Bevy systems
  • GodotAudioPlugin - Add if you want to play sounds and music from Bevy systems
  • GodotSignalsPlugin - Add if you want to respond to Godot signals (button clicks, etc.) in Bevy systems
  • GodotCollisionsPlugin - Add if you want to detect collisions and physics events in Bevy systems
  • GodotInputEventPlugin - Add if you want to handle input from Godot in Bevy systems
  • BevyInputBridgePlugin - Add if you prefer Bevy's input API (auto-includes GodotInputEventPlugin)
  • GodotPackedScenePlugin - Add if you want to spawn scenes dynamically from Bevy systems

Convenience Bundles:

  • GodotDefaultPlugins - All plugins enabled (equivalent to old v0.7.x behavior)

Plugin Dependencies

Some plugins automatically include their dependencies:

  • GodotSceneTreeMirroringPlugin automatically includes GodotSceneTreeEventsPlugin
  • BevyInputBridgePlugin automatically includes GodotInputEventPlugin

Benefits of the New System

  1. Smaller binaries - Only compile what you use
  2. Better performance - Skip unused systems
  3. Clearer dependencies - Explicit about what your game needs
  4. Future-proof - Easy to add new optional features

Migration Checklist

  • Quick fix: Add app.add_plugins(GodotDefaultPlugins) to your build_app function
  • Optimization: Replace GodotDefaultPlugins with only the specific plugins you need
  • Test: Ensure all features work correctly with your plugin selection
  • Consider: Whether you can disable some features (e.g., transform sync for physics games)

GodotSignals Resource (Breaking Change)

What Changed

In v0.8.0, the signal connection system has been significantly simplified and improved:

New GodotSignals SystemParam: Signal connections are now handled through a dedicated GodotSignals resource

Migration Path

The main change is switching from the standalone connect_godot_signal function to the new GodotSignals SystemParam.

Before (v0.7.x)

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

fn connect_signals(
    mut scene_tree: SceneTreeRef,
) {
    if let Some(root) = scene_tree.get().get_root() {
        if let Some(button) = root.try_get_node_as::<Button>("UI/MyButton") {
            let mut handle = GodotNodeHandle::from_instance_id(button.instance_id());
            // Old function signature required SceneTreeRef parameter
            connect_godot_signal(&mut handle, "pressed", &mut scene_tree);
        }
    }
}
}

After (v0.8.0)

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

fn connect_signals(
    mut scene_tree: SceneTreeRef,
    signals: GodotSignals,  // ← New SystemParam
) {
    if let Some(root) = scene_tree.get().get_root() {
        if let Some(button) = root.try_get_node_as::<Button>("UI/MyButton") {
            let mut handle = GodotNodeHandle::from_instance_id(button.instance_id());
            // New simplified API
            signals.connect(&mut handle, "pressed");
        }
    }
}
}

Breaking Changes

  1. Function signature changed: connect_godot_signal no longer requires SceneTreeRef parameter
  2. New SystemParam required: Add GodotSignals parameter to systems that connect signals
  3. Recommended API change: Use signals.connect() instead of direct connect_godot_signal() calls

Migration Checklist

  • Add GodotSignals parameter to systems that connect signals
  • Replace connect_godot_signal(&mut handle, signal_name, &mut scene_tree) with signals.connect(&mut handle, signal_name)
  • Remove unused SceneTreeRef parameters if they were only used for signal connections
  • Test that all signal connections work correctly with the new system

Summary

The v0.8.0 signal system simplifies signal connections while improving performance. The main migration step is:

  1. Add GodotSignals parameter to systems that connect signals
  2. Replace connect_godot_signal(&mut handle, signal, &mut scene_tree) with signals.connect(&mut handle, signal)
  3. Remove unused SceneTreeRef parameters

The signal event handling (EventReader<GodotSignal>) remains unchanged, so only the connection setup needs to be updated.

Multithreaded Bevy and #[main_thread_system] (New Feature)

What's New

In v0.8.0, godot-bevy now enables Bevy's multithreaded task executor by default, allowing systems to run in parallel for better performance. However, since Godot's APIs are not thread-safe, we've introduced the #[main_thread_system] attribute to mark systems that must run on the main thread.

Key Changes:

  1. Multithreaded Bevy enabled: Systems can now run in parallel by default
  2. New #[main_thread_system] attribute: Mark systems that use Godot APIs
  3. Better performance: ECS systems can utilize multiple CPU cores

Migration Path

Most existing code will continue to work without changes, but you should add the #[main_thread_system] attribute to any system that directly calls Godot APIs.

When to Use #[main_thread_system]

Add this attribute to systems that:

  • Use SceneTreeRef or other Godot resources
  • Call any Godot API functions that are not thread-safe

Examples

Systems that need #[main_thread_system]:

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

// ✅ Using SceneTreeRef - needs main thread
#[main_thread_system]
fn spawn_enemy(
    mut commands: Commands,
    scene_tree: SceneTreeRef,
    enemy_spawner: Res<EnemySpawner>,
) {
    if let Some(scene) = scene_tree.get().get_root() {
        // Spawn enemy logic using Godot APIs
    }
}

// ✅ Calling non-thread-safe Godot APIs - needs main thread
#[main_thread_system]
fn play_sound_effects(
    mut audio_events: EventReader<AudioEvent>,
    audio_player: Res<AudioStreamPlayer>,
) {
    for event in audio_events.read() {
        // Direct Godot API calls are not thread-safe
        audio_player.play();
    }
}
}

Benefits

  1. Better Performance: ECS systems can now utilize multiple CPU cores
  2. Explicit Threading: Clear distinction between main-thread and multi-thread systems
  3. Safety: Prevents accidental concurrent access to Godot APIs
  4. Scalability: Better performance on multi-core systems

Migration Checklist

  • Review existing systems: Identify which systems use Godot APIs
  • Add #[main_thread_system]: Mark systems that use SceneTreeRef or call non-thread-safe Godot APIs
  • Test performance: Verify that multithreading improves your game's performance
  • Consider refactoring: Separate pure ECS logic from Godot API calls for better parallelization

Common Patterns

Pattern 1: Separate data processing from rendering

#![allow(unused)]
fn main() {
// Multi-threaded: Process game logic
fn calculate_damage(
    mut health_query: Query<&mut Health>,
    damage_events: EventReader<DamageEvent>,
) {
    // Pure ECS logic - runs on any thread
}

// Main thread: Use SceneTreeRef for scene management
#[main_thread_system]
fn update_scene_structure(
    scene_tree: SceneTreeRef,
    spawn_events: EventReader<SpawnEvent>,
) {
    // SceneTreeRef access - runs on main thread
}
}

Pattern 2: Use events to bridge threads

#![allow(unused)]
fn main() {
// Multi-threaded: Game logic generates events
fn enemy_ai_system(
    mut attack_events: EventWriter<AttackEvent>,
    enemy_query: Query<&Transform, With<Enemy>>,
) {
    // Send events instead of directly calling Godot APIs
}

// Main thread: Handle events with non-thread-safe Godot APIs
#[main_thread_system]
fn handle_attack_events(
    mut attack_events: EventReader<AttackEvent>,
    audio_player: Res<AudioStreamPlayer>,
) {
    // Process events using non-thread-safe Godot APIs
    for event in attack_events.read() {
        audio_player.play();
    }
}
}

Summary

The multithreaded Bevy feature significantly improves performance by allowing systems to run in parallel. The main migration step is adding #[main_thread_system] to systems that use Godot APIs, ensuring thread safety while maximizing performance.

BevyBundle Enhanced Property Mapping (New Feature)

What's New

In v0.8.0, the BevyBundle macro has been significantly enhanced with more flexible property mapping options:

  1. Struct Component Mapping: Map multiple Godot properties to fields in a struct component
  2. Transform Functions: Apply transformation functions to convert values during mapping
  3. Improved Syntax: More intuitive syntax for single and multi-field mappings

New Mapping Options

Struct Component Mapping

You can now map multiple Godot properties to fields in a struct component:

#![allow(unused)]
fn main() {
#[derive(Component)]
struct Stats {
    health: f32,
    mana: f32,
    stamina: f32,
}

#[derive(GodotClass, BevyBundle)]
#[class(base=CharacterBody2D)]
#[bevy_bundle((Player), (Stats { health: max_health, mana: max_mana, stamina: max_stamina }))]
pub struct PlayerCharacter {
    base: Base<CharacterBody2D>,
    #[export] max_health: f32,
    #[export] max_mana: f32,
    #[export] max_stamina: f32,
}
}

Transform Functions

Apply transformation functions to convert Godot values before assigning to components:

#![allow(unused)]
fn main() {
fn percentage_to_fraction(value: f32) -> f32 {
    value / 100.0
}

#[derive(GodotClass, BevyBundle)]
#[class(base=Node2D)]
#[bevy_bundle((Enemy), (Health: health_percentage))]
pub struct Enemy {
    base: Base<Node2D>,
    #[export]
    #[bundle(transform_with = "percentage_to_fraction")]
    health_percentage: f32,  // Editor shows 0-100, component gets 0.0-1.0
}
}

Backwards Compatibility

All existing v0.7.x BevyBundle syntax remains fully supported:

#![allow(unused)]
fn main() {
// Still works in v0.8.0
#[bevy_bundle((Player), (Health: max_health))]
}

Benefits

  • Better Component Design: Create struct components that group related data
  • Editor-Friendly Values: Use transform functions to convert between editor-friendly and system-friendly values
  • Type Safety: All mappings are verified at compile time
  • Flexibility: Mix and match different mapping styles as needed

For complete documentation on the new features, see the Custom Node Markers section.