[[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