Rust Game Development with Bevy: A Complete Tutorial

Introduction

The Rust programming language is getting more attention in game development due to its performance, features like advanced compile-time memory safety without a garbage collector and zero-cost abstractions, which in turn make it suitable for game development. Bevy is a data-driven game engine built in Rust, designed to be simple and intuitive to use while also being very efficient and flexible. Today we are going to experience game development in Rust using the Bevy game engine.

Getting Started

Before getting started with Bevy, make sure that you have Rust installed on your system. If Rust is not already installed, you can install it from the official website with a single command.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

To check if it's installed correctly, you can run:

rustc --version

Now, create a new Rust project using the Cargo package manager:

cargo new bevy_game
cd bevy_game

Then, add Bevy to your dependencies in Cargo.toml:

[dependencies]
bevy = "0.5"

The Basics of Bevy

The root principle in Bevy is the ECS architecture - Entities, Components, Systems.

  1. Entities: These are objects in your game. They can be anything - players, enemies, power-ups, blocks, NPCs. In an ECS system, you don't try to put any logic into your entities, they are just containers for information.

  2. Components: These are data that are attached to entities. For instance, a player entity might have a Position, a Health, and a Score component.

  3. Systems: These are where you put your game logic. Systems are functions that run every update cycle, and which have read/write access to components.

For a very basic example, imagine a system that increases player's score each second. In Rust with Bevy, it might look like this:

fn increment_score(time: Res<Time>, mut query: Query<&mut Score>) {
    for mut score in query.iter_mut() {
        score.0 += time.delta_seconds();
    }
}

Bevy Life Cycle

Bevy programs are built around a loop, similar to other game and application engines. Each iteration of the loop is called a frame and the loop itself is commonly called the "game loop". A straightforward representation of Bevy's life cycle can be represented as follows:

  1. Application and resource setup
  2. Enter game loop
  3. Loop:
    1. Run systems
    2. Rendering

Note: The "Loop" steps repeat every frame until it is asked to stop.

Creating a Game

Let's create a very simple game to introduce the basics of Bevy development. Our game will simply move a sprite on screen.

First, we will define our Player component:

struct Player;

Next, we will load the sprite for our player and add it to our game:

fn setup(commands: &mut Commands, asset_server: Res<AssetServer>) {
    let texture_handle = asset_server.load("player.png");
    commands.spawn(Camera2dComponents::default());
    commands.spawn(SpriteComponents {
        material: materials.add(texture_handle.into()),
        ..Default::default()
    }).with(Player);
}

We then need to add a system to handle moving the player:

fn player_movement(time: Res<Time>, keyboard_input: Res<Input<KeyCode>>, mut player_query: Query<(&Player, &mut Transform)>) {
    let speed = 2.0;
    
    for (_player, mut transform) in player_query.iter_mut() {
        let dir = Vec3::new(
            (keyboard_input.pressed(KeyCode::D) - keyboard_input.pressed(KeyCode::A)) as f32,
            (keyboard_input.pressed(KeyCode::W) - keyboard_input.pressed(KeyCode::S)) as f32,
            0.0,
        );

        transform.translation += time.delta_seconds() * dir * speed;
    }
}

Finally, we add our systems to our app builder:

fn main() {
    App::build()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup.system())
        .add_system(player_movement.system())
        .run();
}

Conclusion

Bevy's simplicity and safety alongside Rust's performance make it a powerful tool for game development. While this guide only scratches the surface of what you can do with Bevy, it provides a foundation to start working on games in Bevy.

FAQ

1. What platforms does Bevy support?

Bevy currently supports Windows, MacOS, and Linux, and has experimental support for WebAssembly (Wasm). Bevy also aims to support iOS and Android in the future.

2. What types of games can I make with Bevy?

You can create a variety of games ranging from simple 2D games to complex 3D simulations.

3. How can I extend Bevy's functionality?

Bevy is designed to be modular, allowing you to add your own systems, components, and resources easily. Bevy's plugin system also allows you to package and share functionalities easily with other developers.

4. Is Rust a good language for game development?

Rust brings a lot to the table for game development. Its performance and memory safety make it a strong choice for high-performance games, and its powerful type system and package management make it strong for prototyping and iteration. It's still a young language, but it's growing fast in game development circles.

5. Is Bevy a good choice for beginners in game development?

Bevy's ease of use and structured approach make it relatively accessible for beginners to game development. Its active and friendly community paired with detailed documentation are also instrumental in making learning Bevy a comfortable experience for beginners.