Bevy 0.16 状态管理初学者指南

ZoOL 撰写的 Bevy 状态系统完整入门教程,目标版本 0.16。

核心观点

状态是应用程序级的有限状态机,决定系统执行流程,与属于特定实体的组件不同,状态是影响每帧运行哪些系统的全局资源。

状态定义

#[derive(States, Default, Debug, Clone, Eq, PartialEq, Hash)]
enum GameState {
    #[default]
    MainMenu,
    Loading,
    InGame,
    Paused,
    GameOver,
}

必需 trait: States + Default + Debug + Clone + Eq + PartialEq + Hash

0.16 新 API

// 旧: add_state::<T>()
// 新: init_state::<T>() — 自动使用 Default 作为初始状态
App::new()
    .init_state::<GameState>()
    .init_state::<PausedState>()

多状态系统

可定义多个独立状态类型,同时管理不同关注点:

  • AppState - 应用程序流(Menu / InGame / Settings)
  • PausedState - 暂停状态(Running / Paused)

运行条件

.add_systems(Update, (
    menu_system.run_if(in_state(GameState::MainMenu)),
    gameplay_system.run_if(in_state(GameState::InGame)),
    pause_system.run_if(in_state(GameState::Paused)),
))

组合多状态:

active_gameplay_system
    .run_if(in_state(AppState::InGame))
    .run_if(in_state(PausedState::Running))

转换调度

调度用途运行次数
OnEnter(S)初始化、生成实体、加载资源每次进入时一次
OnExit(S)清理、保存数据、资源管理每次离开时一次
Update + run_if(in_state(S))每帧运行的逻辑每帧

状态转换

fn handle_menu_input(
    mut next_state: ResMut<NextState<GameState>>,
    input: Res<ButtonInput<KeyCode>>,
) {
    if input.just_pressed(KeyCode::Enter) {
        next_state.set(GameState::InGame);
    }
}

重要: 状态转换发生在 StateTransition 调度期间,在 PreUpdate 之后、Update 之前。

0.16 新功能: StateScoped

// 启用
app.enable_state_scoped_entities::<GameState>();
 
// 使用
commands.spawn((
    StateScoped(GameState::InGame),
    Player,
    Health { current: 100, max: 100 },
));

`离开状态时自动销毁实体**,无需手动编写 cleanup 系统。

完整示例

教程包含一个可运行的完整游戏状态机示例,涵盖:

  • MainMenuInGamePausedGameOver 循环
  • StateScoped 自动清理
  • OnEnter / OnExit 设置与销毁
  • 多状态组合运行条件

文件位置

/Users/zool/workspace/ZoOL's Valut/Bevy 0.16 状态管理初学者指南.md