用途
当一个新的 Bevy 应用需要一个 crate、一个版本锁定,以及在玩法系统出现之前为功能代码预留位置时,加载本技能。文中提到的文件夹名称只是布局习惯,并非引擎类型。
主要能力
ECS 形态
- Crate:使用 cargo new 创建,并添加锁定在 0.20 的 bevy 依赖。
- Features:默认会引入窗口、输入和渲染。只有当二进制应保持无头时才关闭 default-features。
- App shell:App::new()、插件,然后在该 App 上添加系统和资源。
- 代码布局:在普通 Rust 模块中按功能拆分。引擎并不要求特定的目录树。
锁定 crate 并保持 dev 构建可用
Bevy 默认的 debug profile 会以未优化方式编译依赖,因此 dev profile 提升依赖的 opt-level,同时让游戏 crate 本身仍易于单步调试。
[package]
name = "game"
version = "0.1.0"
edition = "2021"
[dependencies]
bevy = "0.20"
[profile.dev]
opt-level = 1
[profile.dev.package."*"]
opt-level = 3
无头工具 crate 会去掉默认 feature 集,只添加该二进制实际调用的内容。
[dependencies]
bevy = { version = "0.20", default-features = false }
带窗口的游戏会接入 DefaultPlugins 和一个功能插件。GameConfig 是在第一个 schedule 运行前插入的应用级状态。
#[derive(Resource)]
struct GameConfig {
title: String,
}
fn boot(config: Res<GameConfig>) {
let _title = config.title.as_str();
}
pub struct GamePlugin;
impl Plugin for GamePlugin {
fn build(&self, app: &mut App) {
app.insert_resource(GameConfig {
title: "Game".to_string(),
})
.add_systems(Startup, boot);
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(GamePlugin)
.run();
}
第二个模块拥有一个功能。插件就是边界。系统留在声明其组件的模块中。
#[derive(Component)]
struct Player;
fn spawn_player(mut commands: Commands) {
commands.spawn(Player);
}
pub struct PlayerPlugin;
impl Plugin for PlayerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, spawn_player);
}
}
布局选择
| 需求 | Bevy 惯用法 |
|---|---|
| 新建 crate | cargo new 加上 bevy 依赖 |
| 窗口、输入、渲染 | bevy crate 上的 Cargo features 和 profiles |
| 功能拆分 | 由你选择的 Rust 模块 |
| 项目文件 | Cargo.toml 加上 Rust 模块 |
使用方式
范围
在 Cargo.toml 中锁定 bevy = "0.20"。DefaultPlugins 会引入该 crate feature 所启用的窗口、输入和音频后端。
Workspace 成员和命名 linter 都是普通 Cargo 内容。本技能不会发明文件夹强制器。
专家知识增量
目标:Bevy 0.20+。
核心 ECS 类型:Component、Resource、Query、Commands、Startup、Update、Fixed / FixedMain(bevy::time)、.chain()、Plugin、DefaultPlugins、MinimalPlugins。消息和观察者属于一个归属槽位,其 API 见 bevy-events-messaging。除非 .chain() 对冲突的系统排序,否则系统会并行运行。
谁拥有状态
| 部分 | 所有者 | 谁可以修改 | Schedule |
|---|---|---|---|
| 单个实体上的数据 | Component | 一个系统通过 Query | Update 或 FixedMain |
| 整个应用的一个值 | Resource | 一个系统 | Startup 插入,然后由读取它的 schedule |
| 生成 | Commands | 执行生成的系统 | 初始世界用 Startup;运行时生成用后续 schedule |
| 不能竞态的顺序 | .chain() | 仅被链式的系统 | 同一 schedule |
| 插件边界 | Plugin | build 注册系统和资源 | 应用运行之前 |
借用规则:两个系统若都对同一组件进行可变查询,必须排序(.chain())或拆分,使只有一个写入。不相关的系统保持并行。这是 ECS 页面规则,不是对 system-set API 的猜测。
决策
| 如果你需要 | 这样做 | 不要 |
|---|---|---|
| 每实体数据 | Component + Query | 放在 World 之外的自由浮动 struct |
| 应用级配置或时钟 | Resource | 把值复制到每个实体上 |
| 稳定的模拟步长 | FixedMain 和 Fixed 时钟 | 把模拟绑定到原始帧 Update |
| 启动时生成 | Startup 中的 Commands | 从渲染系统生成 |
| 非共享状态的跨系统通知 | bevy-events-messaging 中的消息或观察者槽位 | 在本文件中发明新的事件类型 |
| 文档集合之外的 API | 先查官方文档 | 编造的 crate 或 API |
加载 references/ecs-ownership.md 查看归属表。
NEVER
- 不要在文档 URL 中锁定 /0.20.0/。原因:链接保持在 latest;0.20 目标只在正文中陈述一次。
- 不要从两个无序系统对同一组件进行可变查询。原因:除非 .chain() 排序,否则 schedule 会并行运行系统。
- 不要在此发明物理、导航、网络或 XR crate。原因:本技能只命名有文档记录的 Bevy crate 和模块。
- 此 crate 中不要使用场景树项目类型或 autoload 单例。原因:Bevy 代码保持惯用 ECS,并只使用有文档记录的 Bevy API。
- 不要发明本技能所列 profiles 之外的 Cargo features。原因:Bevy 代码保持惯用 ECS,并只使用有文档记录的 Bevy API。
- 不要使用第三方 crate。原因:Bevy 代码保持惯用 ECS,并只使用有文档记录的 Bevy API。
适用场景
- 启动带窗口的 Bevy 应用。
- 启动无头(headless)的 Bevy 应用。
- 在玩法系统存在之前搭建 crate、版本锁定和功能代码位置。
限制
- 目标为 Bevy 0.20+。
- 只命名有文档记录的 Bevy crate 和模块,不涉及物理、导航、网络或 XR crate。
- 不使用第三方 crate。
- 不发明 Cargo features 或 API。
- 消息和观察者的 API 需参考 bevy-events-messaging,本技能不展开。
参考
- bevy-master 上的库锁定保持 0.20。
官方文档
- Getting started - cargo new / cargo add bevy。
- Apps - App::new()、World 和 Schedule。
- Examples - latest 分支上的示例树。
相关技能
前置:
- bevy-master - 库锁定以及接下来加载哪个 Bevy 技能。
互补:
- bevy-ecs-composition - crate 存在之后的实体、组件和系统。
下游 / 消费者:
- bevy-app-resources - App 存在之后的应用级资源。
主控:
- bevy-master - Bevy 路由器。