Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
中文简介正在准备中。当前显示原文说明。
Use when writing Bevy (Rust ECS) game code - pin and respect the repo's minor version, plugins/systems/queries/resources, schedules with FixedUpdate and Time, states and run conditions, messages vs observers across versions, required components, testing systems with App::update and a manual time strategy, compile-time and allocation hygiene.
中文简介正在准备中。当前显示原文说明。
查看安装方法安装前可以查看交给 AI 代理的指令内容。
Bevy is a code-first Rust engine built on an ECS: entities are IDs, components are plain structs, systems are ordinary functions whose parameters declare the data they touch, and the scheduler runs them in parallel where it can. Bevy also ships a breaking release every few months — the API you remember may not be the API the repository pins.
Core principle: The repository's pinned Bevy minor is the API. Systems are small functions over queries, simulation runs in FixedUpdate, and every system is testable by building an App and calling update().
# Cargo.toml — for 0.x crates "0.18" already means >=0.18.0, <0.19.0: the minor is pinned
bevy = "0.18"
Read Cargo.lock for the resolved version and that version's migration guide before writing code. Never bump the minor in a feature task. Changes that bite code written from memory:
| Since | Change |
|---|---|
| 0.15 | Time::delta_seconds() → delta_secs(); required components (#[require(...)]) replace most bundles; Single<...> system parameter |
| 0.16 | Query::single() / get_single() return Result; systems may return Result and use ? |
| 0.17 | Buffered events renamed to messages: #[derive(Message)], MessageWriter::write, MessageReader::read, app.add_message::<T>(); observers use On<T> for entity events |
use bevy::prelude::*;
#[derive(Component, Default)]
#[require(Transform, Velocity)]
pub struct Player;
#[derive(Component, Default, Deref, DerefMut)]
pub struct Velocity(pub Vec2);
#[derive(Resource)]
pub struct MoveTuning { pub speed: f32 }
#[derive(Message)]
pub struct PlayerHit { pub damage: u32 }
pub struct MovementPlugin;
impl Plugin for MovementPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(MoveTuning { speed: 220.0 })
.insert_resource(Time::<Fixed>::from_hz(60.0))
.add_message::<PlayerHit>()
.add_systems(Update, read_input.run_if(in_state(GameState::Playing)))
.add_systems(FixedUpdate, (apply_velocity, resolve_hits).chain());
}
}
fn read_input(keys: Res<ButtonInput<KeyCode>>, tuning: Res<MoveTuning>, mut player: Single<&mut Velocity, With<Player>>) {
let x = keys.pressed(KeyCode::KeyD) as i8 as f32 - keys.pressed(KeyCode::KeyA) as i8 as f32;
player.0 = Vec2::new(x * tuning.speed, player.0.y);
}
fn apply_velocity(time: Res<Time>, mut q: Query<(&mut Transform, &Velocity)>) {
for (mut t, v) in &mut q {
t.translation += (v.0 * time.delta_secs()).extend(0.0); // in FixedUpdate, Time is the fixed clock
}
}
main.rs only adds plugins..chain(), .before()/.after() or SystemSets, not by hoping.Update, simulate in FixedUpdate (default 64 Hz; set it explicitly), render-side smoothing in Update/PostUpdate (game-loop-and-time).Commands are deferred: an entity spawned in a system is visible to queries only after the command flush (the next sync point), not later in the same system.Changed<T>, Added<T>, Ref<T>::is_changed) instead of recomputing every frame; filter queries (With, Without) so systems touch only what they need.Res<T> reads run in parallel; ResMut<T> serialises every system that touches T — keep shared mutable resources few.#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
pub enum GameState { #[default] Loading, Menu, Playing, Paused }
app.init_state::<GameState>()
.add_systems(OnEnter(GameState::Playing), spawn_level)
.add_systems(OnExit(GameState::Playing), despawn_level);
Entities spawned for a state are cleaned up on exit (a marker component plus a despawn system, or the state-scoped entity helpers of the pinned version).
#[cfg(test)]
mod tests {
use super::*;
use bevy::time::TimeUpdateStrategy;
use std::time::Duration;
fn app() -> App {
let mut app = App::new();
app.add_plugins(MinimalPlugins)
.insert_resource(TimeUpdateStrategy::ManualDuration(Duration::from_secs_f64(1.0 / 60.0)))
.insert_resource(Time::<Fixed>::from_hz(60.0))
.add_systems(FixedUpdate, apply_velocity);
app.update(); // the first update has zero delta; prime the clock
app
}
#[test]
fn moves_by_speed_times_elapsed_time() {
let mut app = app();
let e = app.world_mut().spawn((Transform::default(), Velocity(Vec2::new(60.0, 0.0)))).id();
for _ in 0..60 { app.update(); }
let x = app.world().get::<Transform>(e).unwrap().translation.x;
assert!((x - 60.0).abs() < 1.5, "x = {x}");
}
}
MinimalPlugins gives time and the schedule runner without a window or GPU — runs anywhere cargo test runs.TimeUpdateStrategy::ManualDuration; never sleep.write_message on the pinned version, send_event before 0.17) and read results back with queries or a resource.world.run_system_once(my_system) for a one-off system call in a test.cargo test && cargo clippy -- -D warnings before hand-off (rule game-build-check); Bevy projects commonly allow clippy::too_many_arguments and clippy::type_complexity — follow the repository's lint config.opt-level = 1 for the crate and opt-level = 3 for dependencies in [profile.dev]/[profile.dev.package."*"]; the dynamic_linking feature only in dev, never in release builds.run_terminal a long timeout or run it detached to a log.Local<Vec<T>> instead of collecting into a new Vec every frame.MinimalPlugins + ScheduleRunnerPlugin, or DefaultPlugins with no primary window; screenshots need a GPU and a window (game-visual-self-review).delta_seconds, EventWriter, get_single) that does not compile on the pinned one.Update with Time and physics in Update — frame-rate dependent.ResMut on everything, killing parallelism.bevy = "*", a >= range, or a version change in Cargo.toml the task did not ask for.std::thread::sleep or Instant::now() inside systems or tests.还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
中文简介正在准备中。当前显示原文说明。
Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping
中文简介正在准备中。当前显示原文说明。
Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal
中文简介正在准备中。当前显示原文说明。
Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task
中文简介正在准备中。当前显示原文说明。
How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.
中文简介正在准备中。当前显示原文说明。
Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here
中文简介正在准备中。当前显示原文说明。