Add example enum Component usage to ecs_guide (#13777)
# Objective Add example of an enum Component to ecs_guide. Fixes https://github.com/bevyengine/bevy/issues/11344. ## Solution Extended ecs_guide "game" to include an enum tracking whether a player is on a "hot" or "cold" streak. ## Testing Ran example manually. cc @MrGVSV
This commit is contained in:
parent
9cb9969793
commit
6ec1f3e6f8
@ -30,6 +30,7 @@ use bevy::{
|
|||||||
utils::Duration,
|
utils::Duration,
|
||||||
};
|
};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
// COMPONENTS: Pieces of functionality we add to entities. These are just normal Rust data types
|
// COMPONENTS: Pieces of functionality we add to entities. These are just normal Rust data types
|
||||||
//
|
//
|
||||||
@ -46,6 +47,25 @@ struct Score {
|
|||||||
value: usize,
|
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!
|
// RESOURCES: "Global" state accessible by systems. These are also just normal Rust data types!
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -85,20 +105,38 @@ 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.
|
// This system updates the score for each entity with the `Player`, `Score` and `PlayerStreak` components.
|
||||||
fn score_system(mut query: Query<(&Player, &mut Score)>) {
|
fn score_system(mut query: Query<(&Player, &mut Score, &mut PlayerStreak)>) {
|
||||||
for (player, mut score) in &mut query {
|
for (player, mut score, mut streak) in &mut query {
|
||||||
let scored_a_point = random::<bool>();
|
let scored_a_point = random::<bool>();
|
||||||
if scored_a_point {
|
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;
|
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!(
|
println!(
|
||||||
"{} scored a point! Their score is: {}",
|
"{} scored a point! Their score is: {} ({})",
|
||||||
player.name, score.value
|
player.name, score.value, *streak
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
*streak = match *streak {
|
||||||
|
PlayerStreak::Hot(_) | PlayerStreak::None => PlayerStreak::Cold(1),
|
||||||
|
PlayerStreak::Cold(n) => PlayerStreak::Cold(n + 1),
|
||||||
|
};
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{} did not score a point! Their score is: {}",
|
"{} did not score a point! Their score is: {} ({})",
|
||||||
player.name, score.value
|
player.name, score.value, *streak
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,8 +144,8 @@ fn score_system(mut query: Query<(&Player, &mut Score)>) {
|
|||||||
// this game isn't very fun is it :)
|
// this game isn't very fun is it :)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This system runs on all entities with the "Player" and "Score" components, but it also
|
// 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.
|
// accesses the `GameRules` resource to determine if a player has won.
|
||||||
fn score_check_system(
|
fn score_check_system(
|
||||||
game_rules: Res<GameRules>,
|
game_rules: Res<GameRules>,
|
||||||
mut game_state: ResMut<GameState>,
|
mut game_state: ResMut<GameState>,
|
||||||
@ -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
|
// 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
|
// 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:
|
// "startup" system from a "normal" system is how it is registered:
|
||||||
// app.add_systems(Startup, startup_system) Normal: app.add_systems(Update, normal_system)
|
// 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>) {
|
fn startup_system(mut commands: Commands, mut game_state: ResMut<GameState>) {
|
||||||
// Create our game rules resource
|
// Create our game rules resource
|
||||||
commands.insert_resource(GameRules {
|
commands.insert_resource(GameRules {
|
||||||
@ -157,12 +196,14 @@ fn startup_system(mut commands: Commands, mut game_state: ResMut<GameState>) {
|
|||||||
name: "Alice".to_string(),
|
name: "Alice".to_string(),
|
||||||
},
|
},
|
||||||
Score { value: 0 },
|
Score { value: 0 },
|
||||||
|
PlayerStreak::None,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Player {
|
Player {
|
||||||
name: "Bob".to_string(),
|
name: "Bob".to_string(),
|
||||||
},
|
},
|
||||||
Score { value: 0 },
|
Score { value: 0 },
|
||||||
|
PlayerStreak::None,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -189,6 +230,7 @@ fn new_player_system(
|
|||||||
name: format!("Player {}", game_state.total_players),
|
name: format!("Player {}", game_state.total_players),
|
||||||
},
|
},
|
||||||
Score { value: 0 },
|
Score { value: 0 },
|
||||||
|
PlayerStreak::None,
|
||||||
));
|
));
|
||||||
|
|
||||||
println!("Player {} joined the game!", game_state.total_players);
|
println!("Player {} joined the game!", game_state.total_players);
|
||||||
@ -199,7 +241,6 @@ fn new_player_system(
|
|||||||
// "exclusive system".
|
// "exclusive system".
|
||||||
// WARNING: These will block all parallel execution of other systems until they finish, so they
|
// 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.
|
// should generally be avoided if you want to maximize parallelism.
|
||||||
#[allow(dead_code)]
|
|
||||||
fn exclusive_player_system(world: &mut World) {
|
fn exclusive_player_system(world: &mut World) {
|
||||||
// this does the same thing as "new_player_system"
|
// this does the same thing as "new_player_system"
|
||||||
let total_players = world.resource_mut::<GameState>().total_players;
|
let total_players = world.resource_mut::<GameState>().total_players;
|
||||||
@ -216,6 +257,7 @@ fn exclusive_player_system(world: &mut World) {
|
|||||||
name: format!("Player {}", total_players + 1),
|
name: format!("Player {}", total_players + 1),
|
||||||
},
|
},
|
||||||
Score { value: 0 },
|
Score { value: 0 },
|
||||||
|
PlayerStreak::None,
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut game_state = world.resource_mut::<GameState>();
|
let mut game_state = world.resource_mut::<GameState>();
|
||||||
@ -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
|
// 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.
|
// 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`.
|
// In this system's `Local` (`counter`), `T` is `u32`.
|
||||||
// Therefore, on the first turn, `counter` has a value of 0.
|
// Therefore, on the first turn, `counter` has a value of 0.
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user