Skip to content

Begin ECS Tutorial #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion content/learn/book/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
redirect_to = "learn/book/introduction"
+++
insert_anchor_links = "right"
+++
5 changes: 3 additions & 2 deletions content/learn/book/contributing/_index.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
+++
title = "Contributing"
weight = 4
weight = 5
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
[extra]
subtitle = "join the bevy"
+++
Expand All @@ -13,4 +14,4 @@ Bevy is built by volunteers. If you want to help us build the next great game en
* If you are a software developer and you want to help out, check out the [Contributing Code](/learn/book/contributing/code) section.
* If you are good at teaching or writing, consider [contributing to our docs](/learn/book/contributing/docs).

We want Bevy to be a vibrant developer community ... thats actually why we chose the name! A Bevy is a group of birds, just like we are a group of game developers. Join the Bevy!
We want Bevy to be a vibrant developer community ... thats actually why we chose the name! A Bevy is a group of birds, just like we are a group of game developers. Join the Bevy!
3 changes: 2 additions & 1 deletion content/learn/book/contributing/code/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ weight = 1
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
[extra]
long_title = "Contributing Code"
+++

We will expand this section in the near future with more information. For now, [check out the Bevy GitHub repo](https://github.com/bevyengine/bevy).
We will expand this section in the near future with more information. For now, [check out the Bevy GitHub repo](https://github.com/bevyengine/bevy).
3 changes: 2 additions & 1 deletion content/learn/book/contributing/docs/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 2
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
[extra]
long_title = "Contributing Docs"
+++
Expand Down Expand Up @@ -52,4 +53,4 @@ We made an extension to the markdown syntax that makes linking to Rust API docs

## Rust API Docs

Bevy's Rust API Docs are automatically generated from the latest Bevy source code. If you add [Rust documentation comments](https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments) to the Bevy codebase, the API docs will be automatically updated.
Bevy's Rust API Docs are automatically generated from the latest Bevy source code. If you add [Rust documentation comments](https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments) to the Bevy codebase, the API docs will be automatically updated.
112 changes: 112 additions & 0 deletions content/learn/book/ecs/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
+++
title = "ECS Tutorial"
weight = 3
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

This tutorial aims to give both an awareness and practical examples of the following:
* There is a startup scheduler and a normal scheduler ({{rust_type(type="struct", crate="bevy_ecs", name="Schedule", no_mod=true, plural=false)}})
* Startup systems run only ONCE before normal systems
* You can ask normal systems to run once or in an infinite loop
* You can slow the infinite loop by specifying an additional `k` second sleep delay between each loop
- where `k ∈ [0, 9223372036854775807_i64]` seconds (i.e. ~292 billion years)
* A **System** results from calling the inline {{rust_type(type="trait" crate="bevy_ecs", name="IntoForEachSystem" method="system" no_struct=false)}} method on a `fn`. It is a {{rust_type(type="struct", crate="bevy_ecs", name="SystemFn", no_mod=true, plural=false)}} struct that implements the {{rust_type(type="trait", crate="bevy_ecs", name="System", no_mod=true, plural=false)}} trait and encapsulates the original function
* A **System** is always assigned a **stage** in the scheduler
- This dictates the group of systems that it runs in parallel with
- The default normal stage is `stage::UPDATE`
- The default startup stage is `startup_stage::STARTUP`
* Except when certain data constraints exist (i.e. thread local with exclusive access to {{rust_type(type="struct", crate="bevy_ecs", name="World", no_mod=true, plural=false)}} and {{rust_type(type="struct", crate="bevy_ecs", name="Resources", no_mod=true, plural=false)}})
- Startup systems run in no particular order within their **stage** due to parallel execution
- Normal systems run in no particular order within their **stage** due to parallel execution
* Stages exist as an ordered collection
* For finer control of the order of execution within a scheduler
- You can create as many NEW stages in the scheduler as you like
- New stages are added immediately *before* or *after* an existing stage (system defined or custom)
* You can emit and consume custom events
* You can add new **Components** and **Systems** to a running {{rust_type(type="struct", crate="bevy_app", name="App", no_mod=true, plural=false)}}, i.e. after `.run()`
- with `commands:` {{rust_type(type="struct", crate="bevy_ecs", name="Commands", no_mod=true, plural=false)}}
* {{rust_type(type="struct", crate="bevy_app", name="App", no_mod=true, plural=true)}} also have a mechanism for exiting gracefully
Comment on lines +10 to +31
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a much more concise, higher level introduction? In its current form I think it dives into the details way too early. Just listing a giant list of facts is a good way to lose people's attention. I think its better to do that piece by piece as users work through the tutorial.

I think this section should probably just be a single paragraph describing the high level goals of the tutorial and the app we will be building.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I made the intro deliberately dense because I'm using it as a roadmap to make sure I touch on all of those points. I'll definitely make it more incoming-user friendly when I've included these points throughout the tutorial. Maybe I'll do a chapter review section at the end that looks more like this.

I actually created a little MUD battle simulation (Player vs rats) to use as the basis for this tutorial, but it very quickly got out of hand. The things that make it interesting are also the things that make it too long.


## Create Project

First [create](@/learn/book/getting-started/setup/_index.md#create-a-new-rust-executable-project) a new project called `bevy_ecs_tut` and [add](@/learn/book/getting-started/setup/_index.md#add-bevy-to-your-project-s-cargo-toml) <img src="/assets/bevy_logo_dark.svg" style="height: 1em;margin-bottom:-0.1em" alt="BEVY"/> as a dependency.


## AppBuilder

Jump right in and create a simple game. We'll add a single, simple resource and a startup system that shows us how the ECS initiated our resource.

At this stage the app doesn't even loop, it will iterate once and terminate.

{{fileref(name="src/main.rs")}}

```rs
use bevy::prelude::*;

fn main() {
App::build()
.init_resource::<GameState>()
.add_startup_system(print_initial_system.system())
.run();
}

// The struct behind our simple resource
#[derive(Default)]
struct GameState {
current_round: usize,
total_players: usize,
winning_player: Option<String>,
}

// The fn that will become the system that displays our simple resource
fn print_initial_system(state: Res<GameState>) {
println!("Welcome to the Game!
The default GameState is:\n\tCurrent Round: {}
\tTotal Players: {}\n\tWinning Player: {:?}",
state.current_round, state.total_players, state.winning_player);
}
```
{{caption(ref=3.2, desc="A simple App")}}

At the risk of stating the obvious.
```bash
$ cargo run

Welcome to the Game!
The default GameState is:
Current Round: 0
Total Players: 0
Winning Player: None
```
{{caption(ref=3.3, desc="First game output")}}

The values of `GameState` come from the derived `Default` implementation.

{{rust_type(type="struct" crate="bevy_app", name="App" method="build" no_struct=false)}} makes an {{rust_type(type="struct", crate="bevy_app", name="AppBuilder", no_mod=true, plural=false)}} for us, on which we can call inline methods to add **Systems**, **Components**, and configuration.

{{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="init_resource" no_struct=false)}} inserts `GameState` into the {{rust_type(type="struct" crate="bevy_ecs", name="Resources", no_mod=false, plural=false)}} type on {{rust_type(type="struct", crate="bevy_app", name="App", no_mod=true, plural=false)}}, `App::resources<Resources>`, making `GameState` a **Component**.

It can do this because `GameState` derives `Default` and because we anotate the type for the compiler.
We could have done this with {{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="add_resource" no_struct=false)}}.

Then {{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="add_startup_system" no_struct=false)}} takes the system created with {{rust_type(type="trait" crate="bevy_ecs", name="IntoForEachSystem" method="system" no_struct=true)}} and attaches it at the `startup_stage::STARTUP` stage.

Internally, `.add_startup_system(print_initial_system.system())`
is called as `.add_startup_system_to_stage(startup_stage::POST_STARTUP, print_initial_system.system())` ({{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="add_startup_system_to_stage" no_struct=true)}}).

### Startup Schedule

A startup scheduler runs only once and we can add systems to it with {{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="add_startup_system" no_struct=true)}} or {{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="add_startup_system_to_stage" no_struct=true)}}.
The startup scheduler only has two in-built stages, `startup_stage::STARTUP` and `startup_stage::POST_STARTUP`.

### Normal Schedule

Systems assigned to the normal scheduler can run one or more times depending on whether we add a looping mechanism to the App.
They are added to the App with {{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="add_system" no_struct=true)}} or {{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="add_system_to_stage" no_struct=true)}}. The normal stages are `stage::{FIRST, EVENT_UPDATE, PRE_UPDATE, UPDATE, POST_UPDATE, LAST}`.


For both normal and startup there are two functions for adding new stages, either before or after an existing stage. You'll see them soon.
You can add as many stages as you need because they are stored in an ordered collection. I.e. if you added ten stages before `stage::UPDATE` none of them would ever run before `stage::PRE_UPDATE`.
5 changes: 3 additions & 2 deletions content/learn/book/faq/_index.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
+++
title = "Faq"
weight = 5
weight = 6
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
[extra]
long_title = "Frequently Asked Questions"
+++
Expand All @@ -25,4 +26,4 @@ These experiences led me to want the following from a game engine:

None of the engines on the market _quite_ line up with what I'm looking for. And the changes required to make them meet my requirements are either massive in scope, impossible (closed source), or unwelcome (the things I want aren't what the developers or customers want). On top of that, making new game engines is fun!

Bevy is not trying to out-compete other open-source game engines. As much as possible we should be collaborating and building common foundations. If you are an open source game engine developer and you think a Bevy component would make your engine better, one of your engine's components could make Bevy better, or both, please reach out! Bevy is already benefitting massively from the efforts of the Rust gamedev ecosystem and we would love to pay it forward in whatever way we can.
Bevy is not trying to out-compete other open-source game engines. As much as possible we should be collaborating and building common foundations. If you are an open source game engine developer and you think a Bevy component would make your engine better, one of your engine's components could make Bevy better, or both, please reach out! Bevy is already benefitting massively from the efforts of the Rust gamedev ecosystem and we would love to pay it forward in whatever way we can.
10 changes: 9 additions & 1 deletion content/learn/book/getting-started/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 2
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

This section will help you get started on your Bevy journey as quickly as possible. It will walk you through setting up your development environment and writing a simple Bevy app.
Expand Down Expand Up @@ -43,7 +44,14 @@ bevy = "0.1.2" # make sure this is the latest version

This is the current `bevy` crate version:

<a href="https://crates.io/crates/bevy"><img src="https://img.shields.io/crates/v/bevy.svg" style="height: 1.7rem; margin-bottom: 2rem"/></a>
<a href="https://crates.io/crates/bevy"><img src="https://img.shields.io/crates/v/bevy.svg" style="height: 1.7rem"/></a>

But if going to the browser is too slow for you, `cargo` can also fetch it from the command line.
```bash
$ cargo search bevy
bevy = "0.1.3" # A refreshingly simple data-driven ...
```
{{caption(ref=3.1, desc="Truncated output of cargo search")}}

> **_NOTE:_** Currently the project is moving really fast. Specifying the git repository instead of a version might help keeping up to date.
```toml
Expand Down
3 changes: 2 additions & 1 deletion content/learn/book/getting-started/apps/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 2
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

Bevy programs are referred to as {{rust_type(type="struct", crate="bevy_app", name="App", no_mod=true, plural=true)}}. The simplest Bevy app looks like this:
Expand All @@ -22,4 +23,4 @@ Nice and simple right? Copy the code above into your ```main.rs``` file, then ru
cargo run
```

in your project folder. You will notice that ... nothing happens. This is because we haven't told our app to do anything yet! Apps are just empty shells capable of running our application logic. Lets add some logic to our App using Bevy ECS.
in your project folder. You will notice that ... nothing happens. This is because we haven't told our app to do anything yet! Apps are just empty shells capable of running our application logic. Lets add some logic to our App using Bevy ECS.
3 changes: 2 additions & 1 deletion content/learn/book/getting-started/ecs/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 3
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

All app logic in Bevy uses the Entity Component System paradigm, which is often shortened to ECS. ECS is a software pattern that involves breaking your program up into **Entities**, **Components**, and **Systems**. **Entities** are unique "things" that are assigned groups of **Components**, which are then processed using **Systems**.
Expand Down Expand Up @@ -54,7 +55,7 @@ fn main() {

Note the `hello_world.system()` function call. This is a "trait extension method" that converts the `hello_world` function into the {{rust_type(type="trait" crate="bevy_ecs" name="System")}} type.

The {{rust_type(type="trait" crate="bevy_ecs", name="IntoQuerySystem" method="add_system" no_struct=true)}} function adds the system to your App's {{rust_type(type="struct", crate="bevy_ecs", name="Schedule")}}, but we'll cover that more later.
The {{rust_type(type="struct" crate="bevy_app", name="AppBuilder" method="add_system" no_struct=false)}} function adds the system to your App's {{rust_type(type="struct", crate="bevy_ecs", name="Schedule")}}, but we'll cover that more later.

Now run your App again using `cargo run`. You should see `hello world!` printed once in your terminal.

Expand Down
3 changes: 2 additions & 1 deletion content/learn/book/getting-started/plugins/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 4
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

One of Bevy's core principles is modularity. All Bevy engine features are implemented as plugins. This includes internal features like the renderer, but games themselves are also implemented as plugins! This empowers developers to pick and choose which features they want. Don't need a UI? Don't register the {{rust_type(type="struct" crate="bevy_ui", name="UiPlugin")}}. Want to build a headless server? Don't register the {{rust_type(type="struct" crate="bevy_render" name="RenderPlugin")}}.
Expand Down Expand Up @@ -94,4 +95,4 @@ fn main() {
}
```

Try running the app again. It should do exactly what it did before. In the next section, we'll fix the "hello" spam using Resources.
Try running the app again. It should do exactly what it did before. In the next section, we'll fix the "hello" spam using Resources.
3 changes: 2 additions & 1 deletion content/learn/book/getting-started/queries/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 6
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

In Bevy ECS, `Queries` give you direct control over entity iteration. They also provide a few extra filtering options, such as `With<Component>` and `Without<Component>` filters.
Expand Down Expand Up @@ -43,4 +44,4 @@ Now our App should properly greet all `People`, once every two seconds!

Now that we have two different system types, how do you choose which one to use?

This is largely an aesthetic question. Some developers will prefer the simplicity and legibility of **For-Each Systems** and will use them wherever they can. Others will prefer the flexibility of **Query Systems**. There are no right answers here. You should use what works best for you!
This is largely an aesthetic question. Some developers will prefer the simplicity and legibility of **For-Each Systems** and will use them wherever they can. Others will prefer the flexibility of **Query Systems**. There are no right answers here. You should use what works best for you!
3 changes: 2 additions & 1 deletion content/learn/book/getting-started/resources/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 5
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

**Entities** and **Components** are great for representing complex, query-able groups of data. But most Apps will also require "globally unique" data of some kind. In Bevy ECS, we represent globally unique data using **Resources**.
Expand Down Expand Up @@ -68,4 +69,4 @@ The problem is that this system runs once _for each entity_ that has a `Person`

We need a way for our system to "tick" the timer once per update, but greet _everyone_ whenever the `Timer` is finished. Fortunately Bevy ECS has `Queries` for exactly this purpose!

Continue to the next section so we can fix this nasty bug!
Continue to the next section so we can fix this nasty bug!
3 changes: 2 additions & 1 deletion content/learn/book/getting-started/setup/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 1
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

I know you are itching to start making games, but we need to do a _small_ amount of setup first.
Expand Down Expand Up @@ -107,4 +108,4 @@ bevy = "0.1.2" # make sure this is the latest version

Run ```cargo run``` again. The Bevy dependencies should start building. This will take some time as you are essentially building an engine from scratch. You will only need to do a full rebuild once. Every build after this one will be fast!

Now that we have our Bevy project set up, we're ready to start making our first Bevy app!
Now that we have our Bevy project set up, we're ready to start making our first Bevy app!
3 changes: 2 additions & 1 deletion content/learn/book/introduction/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ weight = 1
sort_by = "weight"
template = "book-section.html"
page_template = "book-section.html"
insert_anchor_links = "right"
+++

If you came here because you wanted to learn how to make 2D / 3D games, visualizations, user interfaces, or other graphical applications with Bevy ... you came to the right place! If not, stick around anyway. I promise it will be fun.
Expand Down Expand Up @@ -34,4 +35,4 @@ For a more in-depth introduction, check out the [Introducing Bevy](/news/introdu

Bevy aims to be a general purpose game engine capable of handling any 2D or 3D workload. However Bevy is still in its infancy. <span class="warning">We are currently in the <i>prototyping</i> phase: important features are missing and APIs will change constantly.</span> If you are currently trying to pick an engine for your Next Big Project™, we recommend that you check out [Godot Engine](https://godotengine.org). It is currently much more feature-complete and stable. And it is also free, open-source, and [scriptable with Rust](https://github.com/GodotNativeTools/godot-rust)!

Phew! If you haven't been scared away yet, lets move on to learning some Bevy!
Phew! If you haven't been scared away yet, lets move on to learning some Bevy!
Loading