Skip to content
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

Add example enum Component usage to ecs_guide #13777

Merged
merged 3 commits into from
Jun 25, 2024
Merged
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
68 changes: 55 additions & 13 deletions examples/ecs/ecs_guide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use bevy::{
utils::Duration,
};
use rand::random;
use std::fmt;

// COMPONENTS: Pieces of functionality we add to entities. These are just normal Rust data types
//
Expand All @@ -46,6 +47,25 @@ struct Score {
value: usize,
}

// Enums can also be used as components.
// This component tracks how many consecutive rounds a player has/hasn't scored in.
#[derive(Component)]
enum PlayerStreak {
Hot(usize),
None,
Cold(usize),
}

impl fmt::Display for PlayerStreak {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PlayerStreak::Hot(n) => write!(f, "{n} round hot streak"),
PlayerStreak::None => write!(f, "0 round streak"),
PlayerStreak::Cold(n) => write!(f, "{n} round cold streak"),
}
}
}

// RESOURCES: "Global" state accessible by systems. These are also just normal Rust data types!
//

Expand Down Expand Up @@ -85,29 +105,47 @@ fn new_round_system(game_rules: Res<GameRules>, mut game_state: ResMut<GameState
);
}

// This system updates the score for each entity with the "Player" and "Score" component.
fn score_system(mut query: Query<(&Player, &mut Score)>) {
for (player, mut score) in &mut query {
// This system updates the score for each entity with the `Player`, `Score` and `PlayerStreak` components.
fn score_system(mut query: Query<(&Player, &mut Score, &mut PlayerStreak)>) {
for (player, mut score, mut streak) in &mut query {
let scored_a_point = random::<bool>();
if scored_a_point {
// Accessing components immutably is done via a regular reference - `player`
// has type `&Player`.
//
// Accessing components mutably is performed via type `Mut<T>` - `score`
// has type `Mut<Score>` and `streak` has type `Mut<PlayerStreak>`.
//
// `Mut<T>` implements `Deref<T>`, so struct fields can be updated using
// standard field update syntax ...
score.value += 1;
// ... and matching against enums requires dereferencing them
*streak = match *streak {
PlayerStreak::Hot(n) => PlayerStreak::Hot(n + 1),
PlayerStreak::Cold(_) | PlayerStreak::None => PlayerStreak::Hot(1),
};
println!(
"{} scored a point! Their score is: {}",
player.name, score.value
"{} scored a point! Their score is: {} ({})",
player.name, score.value, *streak
);
} else {
*streak = match *streak {
PlayerStreak::Hot(_) | PlayerStreak::None => PlayerStreak::Cold(1),
PlayerStreak::Cold(n) => PlayerStreak::Cold(n + 1),
};

println!(
"{} did not score a point! Their score is: {}",
player.name, score.value
"{} did not score a point! Their score is: {} ({})",
player.name, score.value, *streak
);
}
}

// this game isn't very fun is it :)
}

// This system runs on all entities with the "Player" and "Score" components, but it also
// accesses the "GameRules" resource to determine if a player has won.
// This system runs on all entities with the `Player` and `Score` components, but it also
// accesses the `GameRules` resource to determine if a player has won.
fn score_check_system(
game_rules: Res<GameRules>,
mut game_state: ResMut<GameState>,
Expand Down Expand Up @@ -139,8 +177,9 @@ fn game_over_system(

// This is a "startup" system that runs exactly once when the app starts up. Startup systems are
// generally used to create the initial "state" of our game. The only thing that distinguishes a
// "startup" system from a "normal" system is how it is registered: Startup:
// app.add_systems(Startup, startup_system) Normal: app.add_systems(Update, normal_system)
// "startup" system from a "normal" system is how it is registered:
// Startup: app.add_systems(Startup, startup_system)
// Normal: app.add_systems(Update, normal_system)
fn startup_system(mut commands: Commands, mut game_state: ResMut<GameState>) {
// Create our game rules resource
commands.insert_resource(GameRules {
Expand All @@ -157,12 +196,14 @@ fn startup_system(mut commands: Commands, mut game_state: ResMut<GameState>) {
name: "Alice".to_string(),
},
Score { value: 0 },
PlayerStreak::None,
),
(
Player {
name: "Bob".to_string(),
},
Score { value: 0 },
PlayerStreak::None,
),
]);

Expand All @@ -189,6 +230,7 @@ fn new_player_system(
name: format!("Player {}", game_state.total_players),
},
Score { value: 0 },
PlayerStreak::None,
));

println!("Player {} joined the game!", game_state.total_players);
Expand All @@ -199,7 +241,6 @@ fn new_player_system(
// "exclusive system".
// WARNING: These will block all parallel execution of other systems until they finish, so they
// should generally be avoided if you want to maximize parallelism.
#[allow(dead_code)]
fn exclusive_player_system(world: &mut World) {
// this does the same thing as "new_player_system"
let total_players = world.resource_mut::<GameState>().total_players;
Expand All @@ -216,6 +257,7 @@ fn exclusive_player_system(world: &mut World) {
name: format!("Player {}", total_players + 1),
},
Score { value: 0 },
PlayerStreak::None,
));

let mut game_state = world.resource_mut::<GameState>();
Expand All @@ -225,7 +267,7 @@ fn exclusive_player_system(world: &mut World) {

// Sometimes systems need to be stateful. Bevy's ECS provides the `Local` system parameter
// for this case. A `Local<T>` refers to a value of type `T` that is owned by the system.
// This value is automatically initialized using `T`'s `FromWorld`* implementation upon the system's initialization upon the system's initialization.
// This value is automatically initialized using `T`'s `FromWorld`* implementation upon the system's initialization.
// In this system's `Local` (`counter`), `T` is `u32`.
// Therefore, on the first turn, `counter` has a value of 0.
//
Expand Down