| name | rust-game-development |
| description | Game development in Rust with Bevy ECS. Use when building games with entity-component-system architecture, managing game state, handling input, rendering 2D/3D scenes, or structuring game logic with systems and resources. |
Rust Game Development with Bevy
Based on Bevy documentation, examples, and the Bevy Cheat Book.
When to Use This Skill
- Building games with the Bevy engine
- Understanding ECS (Entity-Component-System) architecture
- Managing game state and transitions
- Handling player input
- Spawning entities and configuring components
- Writing systems that query and transform game data
Quick Start
[dependencies]
bevy = "0.18"
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, (move_player, check_collisions))
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d::default());
commands.spawn((
Sprite { color: Color::srgb(0.3, 0.3, 0.9), ..default() },
Transform::from_xyz(0.0, 0.0, 0.0),
Player,
Health(100),
));
}
ECS Core Concepts
Entities
Unique IDs that group components:
let entity = commands.spawn((
Transform::default(),
Visibility::default(),
Player,
Name::new("Hero"),
)).id();
commands.entity(entity).despawn();
Components
Data attached to entities:
#[derive(Component)]
struct Player;
#[derive(Component)]
struct Health(f32);
#[derive(Component)]
struct Velocity(Vec2);
#[derive(Component)]
struct Enemy {
kind: EnemyKind,
aggro_range: f32,
}
Systems
Functions that process entities:
fn movement_system(
time: Res<Time>,
mut query: Query<(&Velocity, &mut Transform)>,
) {
for (velocity, mut transform) in &mut query {
transform.translation += velocity.0.extend(0.0) * time.delta_secs();
}
}
Resources
Global singleton data:
#[derive(Resource)]
struct Score(u32);
#[derive(Resource)]
struct GameSettings {
difficulty: f32,
sound_volume: f32,
}
fn update_score(mut score: ResMut<Score>) {
score.0 += 10;
}
Queries
Basic Queries
fn print_names(query: Query<&Name, With<Player>>) {
for name in &query {
println!("{}", name);
}
}
fn damage_enemies(mut query: Query<&mut Health, With<Enemy>>) {
for mut health in &mut query {
health.0 -= 10.0;
}
}
Query Filters
Query<&Transform, (With<Player>, Without<Dead>)>
Query<&Health, Changed<Health>>
Query<&Name, Added<Name>>
Query<&Transform, Or<(With<Player>, With<NPC>)>>
Multiple Queries (Disjoint)
fn combat(
players: Query<(&Transform, &Attack), With<Player>>,
mut enemies: Query<(&Transform, &mut Health), With<Enemy>>,
) {
for (player_tf, attack) in &players {
for (enemy_tf, mut health) in &mut enemies {
let distance = player_tf.translation.distance(enemy_tf.translation);
if distance < attack.range {
health.0 -= attack.damage;
}
}
}
}
Events
#[derive(Event)]
struct CollisionEvent {
entity_a: Entity,
entity_b: Entity,
}
fn detect_collisions(
query: Query<(Entity, &Transform, &Collider)>,
mut events: EventWriter<CollisionEvent>,
) {
events.send(CollisionEvent { entity_a, entity_b });
}
fn handle_collisions(mut events: EventReader<CollisionEvent>) {
for event in events.read() {
println!("Collision: {:?} hit {:?}", event.entity_a, event.entity_b);
}
}
Input Handling
fn player_input(
keyboard: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut Velocity, With<Player>>,
) {
let mut direction = Vec2::ZERO;
if keyboard.pressed(KeyCode::KeyW) { direction.y += 1.0; }
if keyboard.pressed(KeyCode::KeyS) { direction.y -= 1.0; }
if keyboard.pressed(KeyCode::KeyA) { direction.x -= 1.0; }
if keyboard.pressed(KeyCode::KeyD) { direction.x += 1.0; }
for mut velocity in &mut query {
velocity.0 = direction.normalize_or_zero() * SPEED;
}
}
fn mouse_click(buttons: Res<ButtonInput<MouseButton>>) {
if buttons.just_pressed(MouseButton::Left) {
}
}
Game States
#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
enum GameState {
#[default]
Menu,
Playing,
Paused,
GameOver,
}
fn main() {
App::new()
.init_state::<GameState>()
.add_systems(OnEnter(GameState::Playing), setup_game)
.add_systems(OnExit(GameState::Playing), cleanup_game)
.add_systems(Update, game_logic.run_if(in_state(GameState::Playing)))
.run();
}
fn pause_game(
keyboard: Res<ButtonInput<KeyCode>>,
mut next_state: ResMut<NextState<GameState>>,
) {
if keyboard.just_pressed(KeyCode::Escape) {
next_state.set(GameState::Paused);
}
}
Plugins
Modular game organization:
pub struct CombatPlugin;
impl Plugin for CombatPlugin {
fn build(&self, app: &mut App) {
app.add_event::<DamageEvent>()
.add_systems(Update, (apply_damage, check_death));
}
}
App::new()
.add_plugins((DefaultPlugins, CombatPlugin, UIPlugin))
.run();
Reference Map
references/ecs-patterns.md — entity hierarchies, bundles, system ordering
references/rendering-assets.md — sprites, meshes, textures, asset loading
references/game-patterns.md — state machines, timers, spawning, physics
Key References