bevy-ecs
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBevy ECS
Bevy ECS
Structure a Bevy game in Rust around the Entity Component System: the and
plugins, components and resources, systems with queries, scheduling, and
frame-rate-independent updates. Pins Bevy 0.16+ (code targets 0.16; Bevy's API
shifts each minor release — match your ).
AppCargo.toml基于实体组件系统(ECS)使用Rust构建Bevy游戏:涵盖与插件、组件与资源、带查询的系统、调度以及帧率无关的更新。本文针对**Bevy 0.16+**版本(代码以0.16为目标;Bevy的API在每个小版本都会变更——请与你的版本匹配)。
AppCargo.tomlWhen to use
使用场景
- Use when wiring a Bevy , defining
App/Componenttypes, writing systems that query entities, ordering/filtering systems, or fixing borrow-conflict panics and frame-dependent movement.Resource - Use when depends on
Cargo.tomland code callsbevy,App::new(),add_systems, orQuery.Commands
When not to use: this is the ECS core. Deep rendering, custom shaders/
pipelines, UI layout, and audio are separate concerns. For engine-agnostic AI or
procedural algorithms, pair with / .
game-aiprocedural-gen- 适用于配置Bevy 、定义
App/Component类型、编写查询实体的系统、排序/过滤系统,或解决借用冲突 panic 以及帧率相关移动问题的场景。Resource - 当依赖
Cargo.toml且代码调用bevy、App::new()、add_systems或Query时使用。Commands
不适用于: 本文聚焦ECS核心功能。深度渲染、自定义着色器/管线、UI布局和音频属于独立领域。对于引擎无关的AI或 procedural 算法,请搭配 / 使用。
game-aiprocedural-genCore workflow
核心工作流程
- Pin the version. Bevy's API changes every minor release. Set
(or your target) in
bevy = "0.16"and treat the matching docs as truth. EnableCargo.tomlin dev for faster iterative builds.dynamic_linking - Build the .
Appgives windowing, input, rendering, time, etc. Register systems into schedules:App::new().add_plugins(DefaultPlugins)(once) andStartup(every frame).Update - Model data as components, globals as resources. for per-entity data;
#[derive(Component)]for one-of-a-kind data (score, settings, the#[derive(Resource)]clock).Time - Write systems as plain functions. Parameters declare data access: for entities,
Query<...>/Res<T>for resources,ResMut<T>for deferred spawn/despawn. Systems run in parallel when their accesses don't conflict.Commands - Drive motion by so speed is frame-rate independent.
time.delta_secs() - Order only what must be ordered with or explicit constraints; gate systems with
.chain(). Group related setup intorun_ifs. Build withPluginand read the panics — Bevy reports conflicting queries at startup.cargo run
- 锁定版本:Bevy的API每个小版本都会变化。在中设置
Cargo.toml(或你的目标版本),并以对应版本的文档为准。在开发环境中启用bevy = "0.16"以加快迭代构建速度。dynamic_linking - 构建:
App提供窗口、输入、渲染、时间等功能。将系统注册到调度中:App::new().add_plugins(DefaultPlugins)(仅运行一次)和Startup(每帧运行)。Update - 将数据建模为组件,全局数据建模为资源:用于每个实体的数据;
#[derive(Component)]用于唯一数据(分数、设置、#[derive(Resource)]时钟)。Time - 将系统编写为普通函数:参数声明数据访问权限:用于实体,
Query<...>/Res<T>用于资源,ResMut<T>用于延迟生成/销毁实体。当系统的访问权限不冲突时,它们会并行运行。Commands - 通过驱动运动,确保速度与帧率无关。
time.delta_secs() - 仅对必须排序的内容排序,使用或显式约束;用
.chain()控制系统执行。将相关设置分组到run_if中。使用Plugin构建并查看panic信息——Bevy会在启动时报告冲突的查询。cargo run
Patterns
模式示例
1. Cargo.toml + minimal App
1. Cargo.toml + 最小化App
toml
undefinedtoml
undefinedCargo.toml — pin the version; the API differs across minor releases.
Cargo.toml — 锁定版本;不同小版本的API存在差异。
[dependencies]
bevy = "0.16"
[dependencies]
bevy = "0.16"
Dev-only: faster recompiles. (Add the matching dynamic linking setup per the book.)
仅开发环境:更快的重新编译速度。(根据官方文档添加对应的动态链接设置。)
bevy = { version = "0.16", features = ["dynamic_linking"] }
bevy = { version = "0.16", features = ["dynamic_linking"] }
```rust
// main.rs
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins) // window, input, render, time, ...
.add_systems(Startup, setup) // runs once at startup
.add_systems(Update, move_players) // runs every frame
.run();
}
```rust
// main.rs
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins) // 窗口、输入、渲染、时间等功能
.add_systems(Startup, setup) // 启动时仅运行一次
.add_systems(Update, move_players) // 每帧运行
.run();
}2. Components, resources, and spawning
2. 组件、资源与实体生成
rust
#[derive(Component)]
struct Player;
#[derive(Component)]
struct Velocity(Vec2);
#[derive(Resource)]
struct Score(u32);
fn setup(mut commands: Commands) {
commands.insert_resource(Score(0));
// Camera2d is a component with required components (bundles removed in 0.16);
// spawning it pulls in Transform, Camera, etc. automatically.
commands.spawn(Camera2d);
// Spawn an entity as a tuple of components.
commands.spawn((
Player,
Velocity(Vec2::new(150.0, 0.0)),
Transform::from_xyz(0.0, 0.0, 0.0),
));
}rust
#[derive(Component)]
struct Player;
#[derive(Component)]
struct Velocity(Vec2);
#[derive(Resource)]
struct Score(u32);
fn setup(mut commands: Commands) {
commands.insert_resource(Score(0));
// Camera2d是包含必要组件的类型(0.16版本已移除bundle);
// 生成它会自动引入Transform、Camera等组件。
commands.spawn(Camera2d);
// 以组件元组形式生成实体。
commands.spawn((
Player,
Velocity(Vec2::new(150.0, 0.0)),
Transform::from_xyz(0.0, 0.0, 0.0),
));
}3. A system with a query + the Time resource
3. 包含查询与Time资源的系统
rust
// Iterate every entity that has BOTH Velocity and Transform; mutate Transform.
fn move_players(time: Res<Time>, mut query: Query<(&Velocity, &mut Transform)>) {
for (velocity, mut transform) in &mut query {
// delta_secs() is f32 seconds (renamed from delta_seconds() in 0.16).
transform.translation += velocity.0.extend(0.0) * time.delta_secs();
}
}rust
// 遍历所有同时拥有Velocity和Transform组件的实体;修改Transform。
fn move_players(time: Res<Time>, mut query: Query<(&Velocity, &mut Transform)>) {
for (velocity, mut transform) in &mut query {
// delta_secs()是浮点数类型的秒数(0.16版本中从delta_seconds()重命名而来)。
transform.translation += velocity.0.extend(0.0) * time.delta_secs();
}
}4. Query filters (With / Without / Changed)
4. 查询过滤器(With / Without / Changed)
rust
// Only entities tagged Player (the Player component itself isn't read).
fn aim_player(mut q: Query<&mut Transform, With<Player>>) { /* ... */ }
// Disjoint two mutable Transform queries so they don't conflict at runtime.
fn separate(
mut players: Query<&mut Transform, With<Player>>,
mut enemies: Query<&mut Transform, Without<Player>>,
) { /* ... */ }
// React only when Health changed since last run (change detection).
fn on_health_change(q: Query<&Health, Changed<Health>>) {
for health in &q { /* update the HUD, etc. */ }
}rust
// 仅筛选标记有Player的实体(不会读取Player组件本身)。
fn aim_player(mut q: Query<&mut Transform, With<Player>>) { /* ... */ }
// 使两个可变Transform查询互斥,避免运行时冲突。
fn separate(
mut players: Query<&mut Transform, With<Player>>,
mut enemies: Query<&mut Transform, Without<Player>>,
) { /* ... */ }
// 仅在Health自上次运行后发生变化时响应(变更检测)。
fn on_health_change(q: Query<&Health, Changed<Health>>) {
for health in &q { /* 更新HUD等操作 */ }
}5. Resources: read and write
5. 资源:读取与写入
rust
fn add_points(mut score: ResMut<Score>) {
score.0 += 10; // ResMut = write access
}
fn show_score(score: Res<Score>) {
info!("score: {}", score.0); // Res = read access
}rust
fn add_points(mut score: ResMut<Score>) {
score.0 += 10; // ResMut = 写入权限
}
fn show_score(score: Res<Score>) {
info!("score: {}", score.0); // Res = 读取权限
}6. Ordering, run conditions, and plugins
6. 排序、运行条件与插件
rust
fn main() {
App::new()
.add_plugins((DefaultPlugins, GameplayPlugin))
// .chain() forces order: damage resolves before death is checked.
.add_systems(Update, (apply_damage, check_deaths).chain())
// run_if gates a system on a condition each frame.
.add_systems(Update, spawn_wave.run_if(wave_timer_finished))
.run();
}
struct GameplayPlugin;
impl Plugin for GameplayPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(Score(0))
.add_systems(Startup, setup)
.add_systems(Update, (move_players, add_points));
}
}rust
fn main() {
App::new()
.add_plugins((DefaultPlugins, GameplayPlugin))
// .chain()强制顺序:伤害结算先于死亡检查。
.add_systems(Update, (apply_damage, check_deaths).chain())
// run_if根据每帧的条件控制系统是否执行。
.add_systems(Update, spawn_wave.run_if(wave_timer_finished))
.run();
}
struct GameplayPlugin;
impl Plugin for GameplayPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(Score(0))
.add_systems(Startup, setup)
.add_systems(Update, (move_players, add_points));
}
}Pitfalls
常见陷阱
- not found → it was renamed to
delta_seconds()(andtime.delta_secs()) in 0.16. Using the old name fails to compile.elapsed_secs() - Movement speed scales with frame rate → multiply per-frame changes by
. Never assume a fixed frame time.
time.delta_secs() - Panic: "conflicting accesses" / "&mut T and &mut T" → two s in one system both write the same component, or one reads while another writes overlapping entities. Make them disjoint with
Query/With, or useWithout.ParamSet - /
Camera2dBundlenot found → bundles were deprecated in 0.15 and removed in 0.16. Spawn the components directly (SpriteBundle,Camera2d,Sprite); required components fill in the rest.Transform - "trait is not implemented" → you forgot
Component(or#[derive(Component)]for a resource).#[derive(Resource)] - Spawned entity not visible to a later query in the same frame → are deferred and applied at the next sync point. Read the entity in a subsequent system, not the one that spawned it.
Commands - System order assumed but not enforced → systems run in parallel by default.
If must follow
B, addAor an explicit ordering constraint.(A, B).chain() - Copy-pasting older Bevy snippets → APIs shift between minor versions (e.g. the buffered-event API was reworked after 0.16). Verify against the docs for your pinned version; don't mix versions.
- 找不到→ 该方法在0.16版本中重命名为
delta_seconds()(以及time.delta_secs())。使用旧名称会导致编译失败。elapsed_secs() - 移动速度随帧率变化 → 将每帧的变化量乘以。永远不要假设固定帧率。
time.delta_secs() - Panic:"conflicting accesses" / "&mut T and &mut T" → 同一系统中的两个都写入同一组件,或一个读取、另一个写入重叠实体。使用
Query/With使它们互斥,或使用Without。ParamSet - 找不到/
Camera2dBundle→ Bundle在0.15版本中被弃用,0.16版本中被移除。 直接生成组件(SpriteBundle、Camera2d、Sprite);必要组件会自动补充。Transform - "trait is not implemented" → 你忘记添加
Component(对于资源则是#[derive(Component)])。#[derive(Resource)] - 生成的实体在同一帧后续查询中不可见 → 是延迟执行的,会在下一个同步点应用。在后续系统中读取实体,而非生成它的系统。
Commands - 假设系统顺序但未强制执行 → 默认情况下系统并行运行。如果必须在
B之后执行,添加A或显式顺序约束。(A, B).chain() - 复制粘贴旧版Bevy代码片段 → 小版本之间API会变更(例如缓冲事件API在0.16之后被重新设计)。请根据你锁定的版本验证文档;不要混合不同版本。
References
参考资料
- For schedules and ordering,
SystemSet/States/OnEnter, change detection,OnExitlifecycle and sync points,Commandsfor conflicting queries, and a version note on the events/observers API, readParamSet.references/queries-and-scheduling.md
- 关于调度与排序、
SystemSet/States/OnEnter、变更检测、OnExit生命周期与同步点、解决查询冲突的Commands,以及事件/观察者API的版本说明,请阅读ParamSet。references/queries-and-scheduling.md
Related skills
相关技能
- — FSMs/behavior trees/steering as portable concepts to implement in ECS.
game-ai - — noise/RNG/generation algorithms to drive from systems.
procedural-gen - /
pygame-core— lighter-weight engines for smaller projects.love2d-core
- — 可在ECS中实现的可移植概念,如有限状态机/行为树/转向算法。
game-ai - — 可由系统驱动的噪声/随机数生成/生成算法。
procedural-gen - /
pygame-core— 适用于小型项目的轻量级引擎。love2d-core