0Pricing
Learn Rust Coding · Lesson

Spawning and Moving Entities

Put things on screen.

Spawning and Moving Entities is a free Learn Rust Coding lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Commands: Deferred Spawning

You can't mutate the World directly while systems run in parallel. Instead, you queue structural changes through Commands.

Spawning, despawning, and adding components are recorded and applied at the end of the schedule stage, keeping access safe.

fn setup(mut commands: Commands) {
    commands.spawn_empty();
}

Spawning with a Bundle

A bundle is a group of components inserted together. The simplest bundle is just a tuple of components.

Bevy treats each tuple element as a component to attach, so one spawn call can give an entity all its starting data.

fn setup(mut commands: Commands) {
    commands.spawn((
        Position { x: 0.0, y: 0.0 },
        Velocity { x: 1.5, y: 0.0 },
        Player,
    ));
}

A Camera to See

Nothing renders without a camera. For 2D games you spawn a Camera2d in your startup system.

The camera is itself an entity with camera components — another example of ECS uniformity. Everything in the scene is an entity.

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());
}

Spawning a Sprite

To draw something, give an entity a Sprite plus a Transform. The transform holds position, rotation, and scale in world space.

A colored sprite needs no image asset, which is handy for prototyping movement before importing art.

commands.spawn((
    Sprite::from_color(Color::srgb(0.2, 0.7, 1.0), Vec2::new(40.0, 40.0)),
    Transform::from_xyz(0.0, 0.0, 0.0),
    Velocity { x: 80.0, y: 0.0 },
));

Transform vs Custom Position

In real Bevy games you usually move the built-in Transform rather than a custom Position, because rendering reads Transform directly.

The translation is a Vec3. For 2D you change x and y and keep z for draw ordering.

#[derive(Component)]
struct Velocity { x: f32, y: f32 }

fn apply_velocity(mut q: Query<(&mut Transform, &Velocity)>) {
    for (mut tf, vel) in &mut q {
        tf.translation.x += vel.x;
        tf.translation.y += vel.y;
    }
}

Iterating a Query Mutably

To change component data you request &mut T and iterate over &mut query. Each item yields mutable references.

Bevy guarantees no two systems hold conflicting mutable access at once, so this loop is safe even while other systems run.

fn move_all(mut q: Query<&mut Transform, With<Player>>) {
    for mut tf in &mut q {
        tf.translation.x += 1.0;
    }
}

Spawning Many Entities

Loops let you populate a level. Call spawn inside a Rust for loop to create a grid of obstacles or a swarm of enemies.

Each iteration queues its own command, and all of them are applied together at the stage boundary.

fn spawn_row(mut commands: Commands) {
    for i in 0..5 {
        commands.spawn((
            Sprite::from_color(Color::WHITE, Vec2::splat(20.0)),
            Transform::from_xyz(i as f32 * 30.0, 0.0, 0.0),
        ));
    }
}

Despawning Entities

Remove an entity entirely with commands.entity(id).despawn(). This frees its components and recycles the entity index.

You typically despawn from a system that queried the entity's Entity id, for example a bullet that left the screen.

fn cleanup(mut commands: Commands, q: Query<(Entity, &Transform)>) {
    for (e, tf) in &q {
        if tf.translation.y > 600.0 {
            commands.entity(e).despawn();
        }
    }
}

Adding Components Later

You can attach new components to an existing entity at runtime via insert. This is how you give a powered-up player a Shield tag.

Removing works the same way with remove::<T>(). Both are structural changes, so they go through Commands.

commands.entity(player).insert(Shield { hits: 3 });
// later
commands.entity(player).remove::<Shield>();

Z for Layering

In 2D, the z value of a transform's translation decides draw order. Higher z renders on top.

Give the background a low z and the player a higher one so sprites never disappear behind the scenery.

// background behind, player in front
commands.spawn((bg_sprite, Transform::from_xyz(0.0, 0.0, 0.0)));
commands.spawn((player_sprite, Transform::from_xyz(0.0, 0.0, 10.0)));

Wiring It Up

Spawn in Startup, move in Update. The startup system creates the camera and entities once; the update system advances them every frame.

This separation keeps initialization out of your per-frame hot path.

App::new()
    .add_plugins(DefaultPlugins)
    .add_systems(Startup, setup)
    .add_systems(Update, apply_velocity)
    .run();

Quick Check

Think about how structural changes are applied.

Recap: Spawning and Moving

You spawn entities with bundles via Commands, render them with Sprite and Transform, and move them by mutating the transform in an Update system.

Despawn and component insertion are also commands. Next we'll make movement react to keyboard input and real elapsed time.

Frequently asked questions

Is the “Spawning and Moving Entities” lesson free?

Yes — the full text of “Spawning and Moving Entities” is free to read here on the web, and the Learn Rust Coding course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.

What will I learn in “Spawning and Moving Entities”?

Put things on screen. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Learn Rust Coding?

No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Spawning and Moving Entities” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Learn Rust Coding lesson?

Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The ECS Mindset
  2. Spawning and Moving Entities
  3. Handling Input and Time
  4. Collisions and Game State
← Back to Learn Rust Coding