Skip to content

Bevy

UI & GraphicsGame DevelopmentRust

What it is

Bevy is a data-driven game engine for Rust built on an Entity Component System, with a renderer, audio, input, physics integration and hot-reloading assets.

Entities are ids, components are data, and systems are functions that query for components. Bevy schedules systems in parallel automatically based on what they access.

Installation

cargo add bevy

Getting started

The smallest useful thing you can do with it, and what each part means.

Components, systems and queries
use bevy::prelude::*;

#[derive(Component)]
struct Velocity(Vec2);

#[derive(Component)]
struct Player;

fn movement(time: Res<Time>, mut query: Query<(&mut Transform, &Velocity)>) {
    for (mut transform, velocity) in &mut query {
        // Always scale by delta time, or speed depends on frame rate.
        transform.translation += velocity.0.extend(0.0) * time.delta_secs();
    }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, spawn)
        .add_systems(Update, (movement, handle_input))
        .run();
}
Bevy inspects each system's parameters and runs non-conflicting systems in parallel with no manual threading. Two systems writing the same component are automatically serialised.

Advanced usage

Where the library earns its place over a simpler alternative.

Filtered queries, states and events
// With<> and Without<> restrict the query without borrowing the data.
fn player_only(mut q: Query<&mut Transform, (With<Player>, Without<Enemy>)>) { }

#[derive(States, Default, Clone, Eq, PartialEq, Hash, Debug)]
enum GameState { #[default] Menu, Playing, Paused }

#[derive(Event)]
struct ScoreChanged(u32);

App::new()
    .init_state::<GameState>()
    .add_event::<ScoreChanged>()
    .add_systems(Update, gameplay.run_if(in_state(GameState::Playing)))
    .add_systems(OnEnter(GameState::Paused), show_pause_menu);
Without<Enemy> is not just a filter — it is often required to convince the scheduler that two queries cannot alias the same entity, which otherwise panics at runtime.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

error[B0001]: Query conflicts with a previous access
Two query parameters can match the same entity mutably. Separate them with With/Without, or use ParamSet.
Compile times are painful
Turn on the dynamic_linking feature for development builds, and use a fast linker such as lld or mold.

Best practices

  • Multiply movement by time.delta_secs() so behaviour is frame-rate independent.
  • Use With/Without filters to disambiguate overlapping queries and avoid conflicts.
  • Enable dynamic_linking in development — full rebuilds are otherwise very slow.
  • Expect breaking changes: Bevy iterates quickly and pins to exact versions in practice.

Background

Why it exists, and what it was reacting to.

Created by Carter Anderson, Bevy made the ECS pattern approachable through a system API that reads as ordinary Rust functions, and became the centre of Rust game development remarkably quickly.