代码组织

Bevy 代码组织指南

本指南介绍Bevy引擎中推荐的代码组织方式,帮助您构建可维护、可扩展的游戏项目。

模块 (Module) 组织

Rust模块基础

Rust的模块系统是代码组织的基础,在Bevy项目中尤为重要:

// lib.rs 或 main.rs
mod player;
mod enemy;
mod ui;
mod systems;
mod resources;

// 重新导出常用类型
pub use player::PlayerPlugin;
pub use enemy::EnemyPlugin;
pub use ui::UiPlugin;

按功能组织模块

// player/mod.rs
mod components;
mod systems;
mod resources;

pub use components::*;
pub use systems::*;
pub use resources::*;

pub struct PlayerPlugin;

impl Plugin for PlayerPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, systems::spawn_player)
           .add_systems(Update, systems::player_movement);
    }
}

按层级组织模块

// 游戏逻辑层
mod game {
    mod player;
    mod enemy;
    mod combat;
}

// 系统层
mod systems {
    mod movement;
    mod collision;
    mod rendering;
}

// 资源层
mod resources {
    mod assets;
    mod config;
    mod state;
}

插件 (Plugin) 系统

插件基础

插件是Bevy中最重要的代码组织方式,将相关功能打包成可重用的单元:

use bevy::prelude::*;

pub struct GamePlugin;

impl Plugin for GamePlugin {
    fn build(&self, app: &mut App) {
        app
            // 添加子插件
            .add_plugins((
                PlayerPlugin,
                EnemyPlugin,
                UiPlugin,
            ))
            // 添加资源
            .init_resource::<GameState>()
            .insert_resource(GameConfig::default())
            // 添加事件
            .add_event::<PlayerDied>()
            .add_event::<EnemySpawned>()
            // 添加系统
            .add_systems(Startup, setup_game)
            .add_systems(Update, game_loop);
    }
}

条件插件

根据配置或环境决定是否启用插件:

pub struct DebugPlugin;

impl Plugin for DebugPlugin {
    fn build(&self, app: &mut App) {
        #[cfg(debug_assertions)]
        {
            app.add_systems(Update, debug_system);
        }
    }
}

// 或者使用特性标志
#[cfg(feature = "debug")]
pub struct DebugPlugin;

impl Plugin for DebugPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Update, debug_system);
    }
}

插件配置

通过配置结构体自定义插件行为:

#[derive(Resource)]
pub struct PlayerConfig {
    pub speed: f32,
    pub health: f32,
    pub spawn_position: Vec3,
}

impl Default for PlayerConfig {
    fn default() -> Self {
        Self {
            speed: 5.0,
            health: 100.0,
            spawn_position: Vec3::ZERO,
        }
    }
}

pub struct PlayerPlugin {
    config: PlayerConfig,
}

impl PlayerPlugin {
    pub fn new(config: PlayerConfig) -> Self {
        Self { config }
    }

    pub fn with_speed(mut self, speed: f32) -> Self {
        self.config.speed = speed;
        self
    }
}

impl Plugin for PlayerPlugin {
    fn build(&self, app: &mut App) {
        app.insert_resource(self.config.clone())
           .add_systems(Startup, spawn_player)
           .add_systems(Update, player_movement);
    }
}

// 使用
fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(PlayerPlugin::new(PlayerConfig {
            speed: 10.0,
            health: 150.0,
            spawn_position: Vec3::new(0.0, 0.0, 0.0),
        }))
        .run();
}

插件依赖管理

pub struct CombatPlugin;

impl Plugin for CombatPlugin {
    fn build(&self, app: &mut App) {
        // 确保依赖的插件已经添加
        if !app.is_plugin_added::<PlayerPlugin>() {
            app.add_plugins(PlayerPlugin);
        }

        app.add_systems(Update, combat_system);
    }
}

Bundle 组件集合

Bundle基础

Bundle是组件的集合,用于一次性添加多个相关组件:

#[derive(Bundle)]
struct PlayerBundle {
    player: Player,
    health: Health,
    transform: Transform,
    sprite: Sprite,
    collider: Collider,
}

impl Default for PlayerBundle {
    fn default() -> Self {
        Self {
            player: Player,
            health: Health::new(100.0),
            transform: Transform::from_xyz(0.0, 0.0, 0.0),
            sprite: Sprite::new(Vec2::new(32.0, 32.0)),
            collider: Collider::circle(16.0),
        }
    }
}

参数化Bundle

#[derive(Bundle)]
struct PlayerBundle {
    player: Player,
    health: Health,
    transform: Transform,
    sprite: Sprite,
    collider: Collider,
}

impl PlayerBundle {
    pub fn new(position: Vec3, health: f32) -> Self {
        Self {
            player: Player,
            health: Health::new(health),
            transform: Transform::from_translation(position),
            sprite: Sprite::new(Vec2::new(32.0, 32.0)),
            collider: Collider::circle(16.0),
        }
    }

    pub fn with_sprite(mut self, size: Vec2) -> Self {
        self.sprite = Sprite::new(size);
        self
    }

    pub fn with_collider(mut self, radius: f32) -> Self {
        self.collider = Collider::circle(radius);
        self
    }
}

组合Bundle

#[derive(Bundle)]
struct PhysicsBundle {
    transform: Transform,
    velocity: Velocity,
    collider: Collider,
}

#[derive(Bundle)]
struct RenderBundle {
    sprite: Sprite,
    material: Handle<ColorMaterial>,
}

#[derive(Bundle)]
struct PlayerBundle {
    #[bundle]
    physics: PhysicsBundle,
    #[bundle]
    render: RenderBundle,
    player: Player,
    health: Health,
}

// 使用
commands.spawn(PlayerBundle {
    physics: PhysicsBundle {
        transform: Transform::from_xyz(0.0, 0.0, 0.0),
        velocity: Velocity::default(),
        collider: Collider::circle(16.0),
    },
    render: RenderBundle {
        sprite: Sprite::new(Vec2::new(32.0, 32.0)),
        material: materials.add(Color::RED),
    },
    player: Player,
    health: Health::new(100.0),
});

动态Bundle

fn spawn_player(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    let mut bundle = PlayerBundle::default();

    // 根据配置动态修改
    if let Some(texture) = asset_server.get_handle("player.png") {
        bundle.sprite = Sprite::new(Vec2::new(64.0, 64.0));
        bundle.material = materials.add(ColorMaterial::from(texture));
    }

    commands.spawn(bundle);
}

系统集 (SystemSet)

系统集基础

系统集用于组织和排序相关的系统:

#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
enum GameSet {
    Input,
    Movement,
    Combat,
    Rendering,
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .configure_sets(Update, (
            GameSet::Input,
            GameSet::Movement,
            GameSet::Combat,
            GameSet::Rendering,
        ).chain())
        .add_systems(Update, (
            handle_input.in_set(GameSet::Input),
            player_movement.in_set(GameSet::Movement),
            enemy_movement.in_set(GameSet::Movement),
            combat_system.in_set(GameSet::Combat),
            render_system.in_set(GameSet::Rendering),
        ))
        .run();
}

条件系统集

#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
enum GameSet {
    Input,
    Movement,
    Combat,
    Rendering,
}

#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
enum PauseSet {
    Paused,
    Unpaused,
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .configure_sets(Update, (
            GameSet::Input,
            GameSet::Movement,
            GameSet::Combat,
            GameSet::Rendering,
        ).chain())
        .configure_sets(Update, (
            PauseSet::Paused,
            PauseSet::Unpaused,
        ).chain())
        .add_systems(Update, (
            handle_input.in_set(GameSet::Input),
            player_movement.in_set((GameSet::Movement, PauseSet::Unpaused)),
            pause_menu.in_set((GameSet::Input, PauseSet::Paused)),
        ))
        .run();
}

动态系统集

#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
enum GameState {
    Menu,
    Playing,
    Paused,
    GameOver,
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_state::<GameState>()
        .add_systems(Update, (
            menu_system.run_if(in_state(GameState::Menu)),
            game_system.run_if(in_state(GameState::Playing)),
            pause_system.run_if(in_state(GameState::Paused)),
            game_over_system.run_if(in_state(GameState::GameOver)),
        ))
        .run();
}

完整的项目结构示例

目录结构

src/
├── main.rs
├── lib.rs
├── game/
│   ├── mod.rs
│   ├── player/
│   │   ├── mod.rs
│   │   ├── components.rs
│   │   ├── systems.rs
│   │   ├── resources.rs
│   │   └── plugin.rs
│   ├── enemy/
│   │   ├── mod.rs
│   │   ├── components.rs
│   │   ├── systems.rs
│   │   └── plugin.rs
│   └── combat/
│       ├── mod.rs
│       ├── components.rs
│       ├── systems.rs
│       └── plugin.rs
├── systems/
│   ├── mod.rs
│   ├── movement.rs
│   ├── collision.rs
│   └── rendering.rs
├── resources/
│   ├── mod.rs
│   ├── assets.rs
│   ├── config.rs
│   └── state.rs
└── ui/
    ├── mod.rs
    ├── components.rs
    ├── systems.rs
    └── plugin.rs

主入口文件

// main.rs
use bevy::prelude::*;
use game::GamePlugin;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(GamePlugin)
        .run();
}

游戏模块

// game/mod.rs
mod player;
mod enemy;
mod combat;

pub use player::PlayerPlugin;
pub use enemy::EnemyPlugin;
pub use combat::CombatPlugin;

pub struct GamePlugin;

impl Plugin for GamePlugin {
    fn build(&self, app: &mut App) {
        app.add_plugins((
            PlayerPlugin,
            EnemyPlugin,
            CombatPlugin,
        ))
        .add_systems(Startup, setup_game)
        .add_systems(Update, game_loop);
    }
}

fn setup_game(mut commands: Commands) {
    // 游戏初始化逻辑
}

fn game_loop() {
    // 游戏主循环逻辑
}

玩家模块

// game/player/mod.rs
mod components;
mod systems;
mod resources;

pub use components::*;
pub use systems::*;
pub use resources::*;

pub struct PlayerPlugin;

impl Plugin for PlayerPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<PlayerConfig>()
           .add_systems(Startup, spawn_player)
           .add_systems(Update, (
               player_movement,
               player_animation,
               player_health,
           ));
    }
}

// game/player/components.rs
use bevy::prelude::*;

#[derive(Component)]
pub struct Player;

#[derive(Component)]
pub struct PlayerHealth {
    pub current: f32,
    pub maximum: f32,
}

#[derive(Bundle)]
pub struct PlayerBundle {
    pub player: Player,
    pub health: PlayerHealth,
    pub transform: Transform,
    pub sprite: Sprite,
}

// game/player/systems.rs
use bevy::prelude::*;
use super::components::*;

pub fn spawn_player(mut commands: Commands) {
    commands.spawn(PlayerBundle {
        player: Player,
        health: PlayerHealth {
            current: 100.0,
            maximum: 100.0,
        },
        transform: Transform::from_xyz(0.0, 0.0, 0.0),
        sprite: Sprite::new(Vec2::new(32.0, 32.0)),
    });
}

pub fn player_movement(
    keyboard_input: Res<ButtonInput<KeyCode>>,
    mut query: Query<&mut Transform, With<Player>>,
) {
    for mut transform in &mut query {
        if keyboard_input.pressed(KeyCode::KeyW) {
            transform.translation.y += 1.0;
        }
        if keyboard_input.pressed(KeyCode::KeyS) {
            transform.translation.y -= 1.0;
        }
        if keyboard_input.pressed(KeyCode::KeyA) {
            transform.translation.x -= 1.0;
        }
        if keyboard_input.pressed(KeyCode::KeyD) {
            transform.translation.x += 1.0;
        }
    }
}

最佳实践

1. 模块组织

  • 按功能划分模块,而不是按类型
  • 使用清晰的模块层次结构
  • 在模块根文件中重新导出常用类型
  • 避免过深的模块嵌套

2. 插件设计

  • 每个主要功能创建一个插件
  • 使用配置结构体自定义插件行为
  • 实现插件依赖管理
  • 使用条件编译控制插件功能

3. Bundle设计

  • 将经常一起使用的组件组合成Bundle
  • 提供便捷的构造方法
  • 支持参数化配置
  • 使用组合模式构建复杂Bundle

4. 系统集管理

  • 使用系统集组织相关系统
  • 明确定义系统执行顺序
  • 使用条件系统集控制执行
  • 避免系统集之间的循环依赖

5. 代码组织原则

  • 单一职责: 每个模块、插件、系统只负责一个功能
  • 开闭原则: 对扩展开放,对修改封闭
  • 依赖倒置: 依赖抽象而不是具体实现
  • 接口隔离: 提供小而专注的接口

6. 性能考虑

  • 合理使用系统集避免不必要的系统执行
  • 使用Bundle减少实体创建开销
  • 避免在插件初始化时进行复杂计算
  • 使用条件系统减少运行时开销

常见模式

1. 功能模块模式

// 每个功能一个模块
mod player {
    pub struct PlayerPlugin;
    impl Plugin for PlayerPlugin { /* ... */ }
}

mod enemy {
    pub struct EnemyPlugin;
    impl Plugin for EnemyPlugin { /* ... */ }
}

// 主插件组合所有功能
pub struct GamePlugin;
impl Plugin for GamePlugin {
    fn build(&self, app: &mut App) {
        app.add_plugins((
            player::PlayerPlugin,
            enemy::EnemyPlugin,
        ));
    }
}

2. 分层架构模式

// 数据层
mod data {
    mod components;
    mod resources;
}

// 逻辑层
mod logic {
    mod systems;
    mod events;
}

// 表现层
mod presentation {
    mod ui;
    mod rendering;
}

3. 特性模块模式

// 核心功能
mod core {
    pub struct CorePlugin;
}

// 可选功能
#[cfg(feature = "debug")]
mod debug {
    pub struct DebugPlugin;
}

#[cfg(feature = "networking")]
mod networking {
    pub struct NetworkingPlugin;
}

通过合理的代码组织,您可以构建出可维护、可扩展、高性能的Bevy游戏项目。记住,好的代码组织不仅能提高开发效率,还能让团队协作更加顺畅。