Rust bevy standards
Skill kimgoetzke/coding-agent-configs/skills/rust-bevy-standards
My personal skills, agents, and other configuration files for agentic coding.
npx -y skills add kimgoetzke/coding-agent-configs --skill rust-bevy-standardsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Write idiomatic Rust code for applications that use Bevy Engine. Augments rust-standards skill, does not replace it. Use when writing Rust code for a Bevy application.
SKILL.md
4.5 KB, as published. Nobody here has run it
⚠️ Bevy 0.17+ Breaking Changes
-
Material handles wrapped in
MeshMaterial3d<T>, notHandle<T> -
Observer pattern replaces event system (
commands.trigger(),add_observer()) -
Eventsplit intoMessage(buffered) andEvent(observers) -
EventWriter/EventReaderreplaced byMessageWriter/MessageReader(message.write()/messages.read()) -
Observer trigger API changed:
// Old commands.add_observer(|trigger: Trigger<OnAdd, Player>| { info!("Spawned player {}", trigger.target()); }); // New commands.add_observer(|add: On<Add, Player>| { info!("Spawned player {}", add.entity); }); -
Color arithmetic removed; use component extraction instead
⚠️ Bevy 0.18+ Breaking Changes
RenderTargetis now a required component onCamera, not aCamerafield:// Old Camera { target: RenderTarget::Image(handle.into()), ..default() } // New commands.spawn((Camera3d::default(), RenderTarget::Image(handle.into())));BorderRadiusis now a field onNode, not a componentLineHeightis now a required component onText/Text2d/TextSpan; removed fromTextFontAmbientLightresource renamed toGlobalAmbientLight;AmbientLightis now a component onCameraclear_children→detach_all_children,remove_children→detach_children,remove_child→detach_child(same onEntityCommandsandEntityWorldMut)AnimationTarget { id, player }replaced by separateAnimationTargetId(id)andAnimatedBy(player_entity)componentsnext_state.set(...)now always firesOnEnter/OnExit; useset_if_neqfor the old behaviourMaterialPluginfieldsprepass_enabled/shadows_enabledreplaced byMaterialtrait methodsenable_prepass()/enable_shadows()SimpleExecutorremoved; useSingleThreadedExecutorinstead#[reflect(...)]now only supports parentheses, not braces or bracketsAssetLoader,AssetSaver,AssetTransformer,Processnow require#[derive(TypePath)]ronno longer re-exported frombevy_sceneorbevy_asset; add it as a direct dependency- Feature renames:
animation→gltf_animation,bevy_sprite_picking_backend→sprite_picking,bevy_ui_picking_backend→ui_picking,bevy_mesh_picking_backend→mesh_picking
General
- Never delete target binaries — Bevy rebuilds take minutes
Footguns
despawn()orphans children, considerdespawn_recursive()instead- Commands are deferred — world mutations apply at end of schedule; don't read back in the same system what you wrote via commands
- Use
Changed<T>andAdded<T>query filters to skip unchanged components — omitting these is the most common Bevy performance mistake - Use observers (
OnAdd,OnRemove) for component lifecycle reactions; don't poll for these inUpdate
Naming
- No unnecessary abbreviations:
positionnotpos - ECS systems: name ends in
_system - Message handlers: name starts with
handle_, ends in_message
ECS
- Think in data (components) and transformations (systems), not objects and methods
- Components = pure data, no logic
- Systems = pure logic, operate on components
- Events/Messages = communication between systems
- Resources = global state; use sparingly
- Keep components small and focused; one large component defeats ECS cache locality
System Design
Plugin structure
- Break the app into discrete modules using plugins
- All plugin structs must have a
///doc comment explaining their purpose and scope
/// Handles damage processing and death detection.
pub struct CombatPlugin;
impl Plugin for CombatPlugin {
fn build(&self, app: &mut App) {
app
.add_event::<DamageEvent>()
.add_systems(Update, (process_damage, check_death));
}
}
System sets
- Use run conditions (
run_if(in_state(...))) to skip whole systems - Use
OnEnter/OnExitschedules for state transitions, not flags checked inUpdate
System ordering
.add_systems(
Update,
(
// 1. Input
handle_input,
// 2. State changes
process_events,
update_state,
// 3. Derived values
calculate_derived_values,
// 4. Visuals
update_materials,
update_animations,
// 5. UI (last)
update_ui_displays,
),
)