代码组织
Bevy 代码组织指南
本指南介绍Bevy引擎中推荐的代码组织方式,帮助您构建可维护、可扩展的游戏项目。
模块 (Module) 组织
Rust模块基础
Rust的模块系统是代码组织的基础,在Bevy项目中尤为重要:
1 2 3 4 5 6 7 8 9 10 11 12
| mod player; mod enemy; mod ui; mod systems; mod resources;
pub use player::PlayerPlugin; pub use enemy::EnemyPlugin; pub use ui::UiPlugin;
|
按功能组织模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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); } }
|
按层级组织模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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中最重要的代码组织方式,将相关功能打包成可重用的单元:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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); } }
|
条件插件
根据配置或环境决定是否启用插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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); } }
|
插件配置
通过配置结构体自定义插件行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #[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(); }
|
插件依赖管理
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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是组件的集合,用于一次性添加多个相关组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #[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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #[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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #[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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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)
系统集基础
系统集用于组织和排序相关的系统:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #[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(); }
|
条件系统集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #[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(); }
|
动态系统集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #[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(); }
|
完整的项目结构示例
目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 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
|
主入口文件
1 2 3 4 5 6 7 8 9 10 11
| use bevy::prelude::*; use game::GamePlugin;
fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugins(GamePlugin) .run(); }
|
游戏模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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() { }
|
玩家模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| 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, )); } }
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, }
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. 功能模块模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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. 分层架构模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| mod data { mod components; mod resources; }
mod logic { mod systems; mod events; }
mod presentation { mod ui; mod rendering; }
|
3. 特性模块模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| mod core { pub struct CorePlugin; }
#[cfg(feature = "debug")] mod debug { pub struct DebugPlugin; }
#[cfg(feature = "networking")] mod networking { pub struct NetworkingPlugin; }
|
通过合理的代码组织,您可以构建出可维护、可扩展、高性能的Bevy游戏项目。记住,好的代码组织不仅能提高开发效率,还能让团队协作更加顺畅。