-
Notifications
You must be signed in to change notification settings - Fork 379
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
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
## 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 | ||
sunwukonga marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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")}} | ||
sunwukonga marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```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`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.