[[Feb 10, 2022]] I decided to take a look at the Bevy game engine. I had checked out Amethyst a few years prior but it looks like that project has been abandoned in favor of moving the community over to Bevy.
### What is Bevy?
Bevy is a Rust based game engine. It features an [[ECS]] system to enable data-driven game programming.
[Learn more here](https://bevyengine.org/learn/)
## Hello, Bevy
### Installing
First let's add the `bevy` crate as a dependency:
```toml
[dependencies]
bevy = "0.6" # make sure this is the latest version
```
To enable faster compilation you can specify bevy be created as a dynamic library:
```toml
[dependencies]
bevy = { version = "0.6.0", features = ["dynamic"] }
```
Note: You should ensure that you remove `dynamic` before releasing otherwise you need to ship the bevy dylib along with your game.
### Getting started
Create our first program in Bevy:
```rust
fn main() {
App::new()
.add_startup_system(add_people.system())
.add_system(greet_people.system())
.run();
}
// - Components
#[derive(Component)]
struct Person;
#[derive(Component)]
struct Name(String);
// - Systems
fn add_people(mut commands: Commands) {
commands.spawn()
.insert(Person)
.insert(Name("Albert Einstein".to_string()));
commands.spawn()
.insert(Person)
.insert(Name("Isaac Newton".to_string()));
}
fn greet_people(query: Query<&Name, With<Person>>) {
for name in query.iter() {
println!("Hello {}!", name.0);
}
}
```
We start by creating two [[Component]]s, `Person` and `Name`. Person is currently empty but it could include information related to a person. `Name` contains a string to hold the name of *something*, notice how `Name` and `Person` are decoupled, as many things have `Name`s.
Next, we add the two systems `add_people` and `greet_people`. `add_people` spawns new entities with the two components. `greet_people` prints the names of all entities with **both** the `Name` and `Person` component. In this case we only print the names of people, but we could just as easily remove the `Person` requirement to print the names of **anything**.
Note: `add_people` is added as a `startup_system` because we want to ensure it runs before any queries and other systems that require these entities.
### Plugins
A core principle of [[Bevy]] is modularity. As such a lot of features are included as as plugins. You can add plugins to your `App` to add features and even create your own plugins to break up code.
```rust
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(add_people)
.add_system(greet_people)
.run();
}
```
The `DefaultPlugins` includes Bevy default plugins that most people expect from an engine. Such as a game loop, ui components and more.
### Resources
These are how [[Bevy]] manages "globally unique" data of some kind, like timers, assets, renderers, etc.
For example, now that we have a game loop we are greeting people way too often. Instead, we can create a timer and only greet after some elapsed time.
```rust
struct GreetTimer(Timer);
fn greet_people(
time: Res<Time>, mut timer: ResMut<GreetTimer>, query: Query<&Name, With<Person>>) {
// update our timer with the time elapsed since the last update
// if that caused the timer to finish, we say hello to everyone
if timer.0.tick(time.delta()).just_finished() {
for name in query.iter() {
println!("hello {}!", name.0);
}
}
}
```
Then we insert the `GreetTimer` resource into the app:
```rust
app.insert_resource(GreetTimer(Timer::from_seconds(2.0, true)))
```
### References
- This post is essentially my notes from reading https://bevyengine.org/learn/book