The Bevy Game Engine
Tags: rust, gamedev, tutorial
Published: 2020-09-22 19:46:16

NOTE: Since this has been written, Bevy has received many awesome, but sadly, breaking updates. The code in this guide is no longer maintained.

Since rust is The Best Language™, it's no surprise that it's been an attractive choice for game development. Well, when I say game development, I mean game engine development, because there are more rust game engines than actual rust games. Whenever Rust gamedev pops up in my feed, I try the new engine, follow the tutorial, and then go back to using Godot. Most of these engines sacrificed simplicity for modularity and extensibility and were generally not fun to work with.

# What is [Bevy](https://bevyengine.org)?

Bevy is, in its own words, a "A refreshingly simple data-driven game engine built in Rust". It fully delivers on this promise, with possibly the least boilerplate necessary to write a game. Enough about that though, let's actually jump straight in to making! You should:

* be familiar-ish with rust
* have cargo installed with the latest stable rust 

# Writing an ECS app in Bevy

Bevy is based on ECS (Entity-Component-System) architecture, a new way of making games that is multi-threading friendly and brings a boost in performance vs standard models. It's so useful, [Unity](https://docs.unity3d.com/Packages/com.unity.entities@0.14/manual/index.html) is implementing one for their engine. You can learn more on the bevy website, but here's a quick rundown.

### Entities

Entities are collections of distinct components. They are represented by a single id within the bevy engine.

### Components

Components can be whatever you want. They are represented by rust objects (`struct`s or `enums`s), and are attached to entities.

### Systems

Systems are functions. They run on collections of components. For example, the system
```rs
fn my_system(a: &ComponentA, b: &mut ComponentB) {
    //code here
}
```
runs on all entities with both `ComponentA` and `ComponentB` attached. You use `&mut` to gain write access. This system can also be written in "query form" as
```rs
fn my_system(query: mut Query<(&ComponentA, &mut ComponentB)>) {
    for (a, b) in &mut query.iter() {
        //code here
    }
}
```
This function runs only once, with `Query::iter()` giving you an iterator, so you can loop through it manually.

### Resources

Resources are also rust objects, but they aren't associated with any Entity. Instead, they are available for all systems to use. They can be accessed like so:
```rs
fn my_system(
    resA: Res<ResourceA>, 
    resB: ResMut<ResourceB>, 
    query: mut Query<(&ComponentA, &mut ComponentB)>
) {
    for (a, b) in &mut query.iter() {
        //code here
    }
}
```

### Enough talk, let's get started.

Create a new project with `cargo new <project name>`, and open in your editor of choice.

Let's edit `Cargo.toml` and add bevy to our dependencies. As of writing, `bevy 0.2.1` is the latest version.

### *`Cargo.toml`*
```toml
[dependencies]
bevy = "0.2"
```

Start by importing stuff into scope. The `bevy` crate is actually just a helper crate that collects all the libraries most games will need into one.

### *`main.rs`*
```rs
use bevy::prelude::*;
```

To start using bevy, they provide a builder pattern to initialize the app. 
### *`main.rs`*
```rs
fn main() {
    App::build()
        /* setup goes here */
        .run();
}
```

If we run our app now, nothing happens. That's because `App` provides nothing by default. We use `add_default_plugins()` to add basic functionality.

### *`main.rs`*
```rs
fn main() {
    App::build()
        .add_default_plugins()
        .run();
}
```

You should see a gray window pop up. Now, we need to populate our app with entities and components. For this, we use a startup system.

### *`main.rs`*
```rs
fn main() {
    App::build()
        .add_default_plugins()
        .add_startup_system(setup.system())
        .run();
}

fn setup() {
    println!("setup!");
}
```

Unlike regular systems, startup systems are only run once, so you should see `setup!` printed in your console only once.

Now, we can add a sprite. To access our ECS World, we can add arguments to the startup system that will automatically be passed by bevy.

### *`main.rs`*
```rs
fn setup(
    mut commands: Commands, 
    mut materials: ResMut<Assets<ColorMaterial>>
) {
    commands
        .spawn(Camera2dComponents::default())
        .spawn(SpriteComponents {
            material: materials.add(Color::rgb(0.2, 0.2, 0.8).into()),
            transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
            sprite: Sprite::new(Vec2::new(32.0, 32.0)),
            ..Default::default()
        });
}
```
`Commands` is a thread-safe buffer that will execute the commands passed to it to the ECS World.
`ResMut<Assets<ColorMaterial>>` is a mutable handle to the Resource `Assets<ColorMaterial>`, which is a collection of materials that you use to create sprites.
To display sprites, we first have to add a camera entity, which is used by the built in rendering system, and a sprite entity, which gets drawn by the rendering system.
Now you should see a small blue square in the middle of the window.

To wrap up this tutorial, let's add movement to our "game".  We first must create a player marker component (a regular empty struct), and add it to the small square entity.

### *`main.rs`*
```rs
struct Player;

fn setup(
    mut commands: Commands, 
    mut materials: ResMut<Assets<ColorMaterial>>
) {
    commands
        /* cut for brevity */
        .spawn(SpriteComponents /* cut for brevity */)
        .with(Player);
}
```

Next, we create a new system (a regular function) that runs on all entities with `Transform` and `Player`. This system also uses the keyboard input resource, which is created in a built-in system that that's part of `add_default_plugins()`. Additionally, we have to register the system to the builder.

### *`main.rs`*
```rs
fn main() {
    App::build()
        .add_default_plugins()
        .add_startup_system(setup.system())
        .add_system(player_movement.system())
        .run();
}

fn player_movement(
    keyboard_input: Res<Input<KeyCode>>,
    mut query: Query<(&mut Transform, &Player)>,
) {
    for (mut transform, _player) in &mut query.iter() {
        let translation = transform.translation_mut();
        if keyboard_input.pressed(KeyCode::Right) {
            *translation.x_mut() += 1.0;
        }
    }
}
```

Now, when the right arrow key is pressed, our square shifts to the right. You can complete the movement if you'd like, but I'm too lazy to do it here. You can find the completed code here:

{% github EthanYidong/learn-bevy %}

In the next article, we'll start building a more complex game. What are your thoughts on bevy? Please do share!

{% link ethanyidong/bevy-2-space-shooter-the-player-3e7d %}