Game Dev Basics
9 examples to get you started with Rust game development - 7 basic and 2 intermediate.
Prerequisites
cargo new my_game && cd my_game
cargo add bevyTooling: Examples use Bevy on Rust 1.97.0 (edition 2024). Pin Bevy version in
Cargo.toml- APIs evolve per release.
Basic Examples
1. Minimal Bevy app
Window + default plugins bootstrap rendering and input.
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
}DefaultPluginsbundles window, render, input, and asset plugins.Startupsystems run once;Updateruns every frame.Commandsqueue spawns/despawns applied later in schedule.
Related: Bevy Engine
2. Spawn a sprite entity
Entities are IDs with bundles of components.
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
Sprite::from_image(asset_server.load("player.png")),
Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
));
}- Components are plain data; behavior lives in systems querying them.
AssetServerloads asynchronously - handleLoadStatefor placeholders.Transformposition uses right-handed coords; 2D often uses XY plane.
Related: Assets & Scenes
3. Move with keyboard input
Read input resource in an Update system.
fn player_move(
keyboard: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut Transform, With<Player>>,
) {
let mut dir = Vec2::ZERO;
if keyboard.pressed(KeyCode::KeyW) { dir.y += 1.0; }
if keyboard.pressed(KeyCode::KeyS) { dir.y -= 1.0; }
if keyboard.pressed(KeyCode::KeyA) { dir.x -= 1.0; }
if keyboard.pressed(KeyCode::KeyD) { dir.x += 1.0; }
for mut transform in &mut query {
transform.translation += (dir.normalize_or_zero() * 200.0).extend(0.0) * 0.016;
}
}ButtonInputreplaces olderInputAPI in recent Bevy versions.- Multiply movement by delta time for frame-rate independence (example uses rough 0.016).
- Tag components (
Player) narrow queries.
Related: Input & Events
4. Marker component pattern
Zero-sized types tag entities for queries.
#[derive(Component)]
struct Player;- Markers carry no data but enable
With<Player>filters. - Prefer many small components over god structs for cache-friendly ECS.
- Derive
Componentfor registration in app type registry.
5. Time resource
Frame and fixed timestep from Time.
fn debug_fps(time: Res<Time>) {
if time.elapsed_secs().fract() < 0.016 {
println!("delta: {:.4}", time.delta_secs());
}
}delta_secsvaries with FPS - use for rendering interpolation.Time<Fixed>provides fixed dt for physics systems inFixedUpdateschedule.- Never assume 60 FPS in gameplay logic.
Related: Game Loops & Timing
6. Events for decoupled logic
Fire and listen without direct system coupling.
#[derive(Event)]
struct DamageEvent {
amount: f32,
}
fn apply_damage(mut reader: EventReader<DamageEvent>) {
for e in reader.read() {
println!("damage {}", e.amount);
}
}- Events consumed once per frame by default readers.
- Good for UI, audio triggers, and achievement hooks.
- Do not overuse for hot paths with thousands per frame.
Related: Input & Events
7. Run criteria and states
Game flow with state-scoped systems.
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
enum GameState {
#[default]
Menu,
Playing,
}OnEnter(GameState::Playing)spawns level entities.OnExitcleans up to avoid leaked entities.- States serialize menu vs gameplay system sets.
Intermediate Examples
8. Fixed update schedule for physics
Separate physics tick from render frame rate.
// Register system in FixedUpdate with Time<Fixed> for stable integration
// Example: velocity integration uses fixed_delta_seconds()- Physics at 60 Hz fixed while render runs at variable FPS.
- Interpolate rendered transforms between previous and current physics state for smooth visuals.
- See dedicated timing page for accumulator pattern details.
Related: Game Loops & Timing
9. Custom wgpu render pass (concept)
Lower-level rendering when Bevy materials are not enough.
// wgpu: device.create_shader_module, render pass set_pipeline, draw indexed- Bevy uses wgpu internally - escape hatch via custom materials or render graph nodes.
- Useful for compute shaders and procedural effects.
- Higher complexity - profile before leaving Bevy abstractions.
Related: Graphics with wgpu
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.