diff options
Diffstat (limited to 'engine-ecs/examples')
| -rw-r--r-- | engine-ecs/examples/component_changed_event.rs | 78 | ||||
| -rw-r--r-- | engine-ecs/examples/component_events.rs | 64 | ||||
| -rw-r--r-- | engine-ecs/examples/component_relationship.rs | 65 | ||||
| -rw-r--r-- | engine-ecs/examples/component_removed_event.rs | 46 | ||||
| -rw-r--r-- | engine-ecs/examples/error_handling.rs | 79 | ||||
| -rw-r--r-- | engine-ecs/examples/event_loop.rs | 120 | ||||
| -rw-r--r-- | engine-ecs/examples/extension.rs | 70 | ||||
| -rw-r--r-- | engine-ecs/examples/multiple_queries.rs | 85 | ||||
| -rw-r--r-- | engine-ecs/examples/optional_component.rs | 81 | ||||
| -rw-r--r-- | engine-ecs/examples/relationship.rs | 56 | ||||
| -rw-r--r-- | engine-ecs/examples/simple.rs | 42 | ||||
| -rw-r--r-- | engine-ecs/examples/with_local.rs | 70 | ||||
| -rw-r--r-- | engine-ecs/examples/with_sole.rs | 61 |
13 files changed, 917 insertions, 0 deletions
diff --git a/engine-ecs/examples/component_changed_event.rs b/engine-ecs/examples/component_changed_event.rs new file mode 100644 index 0000000..2788505 --- /dev/null +++ b/engine-ecs/examples/component_changed_event.rs @@ -0,0 +1,78 @@ +use engine_ecs::event::component::{Changed, EventMatchExt}; +use engine_ecs::pair::Pair; +use engine_ecs::phase::UPDATE as UPDATE_PHASE; +use engine_ecs::system::observer::Observe; +use engine_ecs::{Component, Query, World}; + +#[derive(Component)] +struct SomeData +{ + num: u64, +} + +#[derive(Component)] +struct Greeting +{ + greeting: String, +} + +fn say_hello(query: Query<(&SomeData, &mut Greeting)>) +{ + for (data, mut greeting) in &query { + println!("{}: {}", greeting.greeting, data.num); + + if greeting.greeting == "Good evening" { + greeting.greeting = "Good morning".to_string(); + greeting.set_changed(); + } + } +} + +fn print_changed_greetings(observe: Observe<'_, Pair<Changed, Greeting>>) +{ + println!("\nChanged greetings:"); + + for evt_match in &observe { + let greeting = evt_match.get_ent_target_comp(); + + println!("A greeting changed to {}", greeting.greeting); + } + + println!(""); +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*UPDATE_PHASE, say_hello); + + world.register_observer(print_changed_greetings); + + world.create_entity(( + SomeData { num: 987_654 }, + Greeting { + greeting: "Good afternoon".to_string(), + }, + )); + + world.create_entity(( + SomeData { num: 345 }, + Greeting { greeting: "Good evening".to_string() }, + )); + + world.step(); + + world.step(); + + for (mut greeting,) in &world.query::<(&mut Greeting,), ()>() { + if greeting.greeting == "Good afternoon" { + greeting.greeting = "Yo yo".to_string(); + greeting.set_changed(); + } + } + + world.step(); + + world.step(); +} diff --git a/engine-ecs/examples/component_events.rs b/engine-ecs/examples/component_events.rs new file mode 100644 index 0000000..7c65630 --- /dev/null +++ b/engine-ecs/examples/component_events.rs @@ -0,0 +1,64 @@ +use engine_ecs::actions::Actions; +use engine_ecs::component::Component; +use engine_ecs::event::component::{Changed, EventMatchExt, Removed}; +use engine_ecs::pair::Pair; +use engine_ecs::phase::UPDATE; +use engine_ecs::system::observer::Observe; +use engine_ecs::{Component, Query, World}; + +#[derive(Debug, Component)] +struct CheeseCrumbs +{ + cnt: usize, +} + +#[derive(Debug, Component)] +struct Cheese +{ + name: &'static str, +} + +fn eat_cheese(query: Query<(&Cheese, &mut CheeseCrumbs)>, mut actions: Actions) +{ + for (cheese_ent_id, (_, mut cheese_crumbs)) in query.iter_with_euids() { + println!("Eating cheese!"); + + cheese_crumbs.cnt += 40; + cheese_crumbs.set_changed(); + + actions.remove_components(cheese_ent_id, [Cheese::id()]); + } +} + +fn on_cheese_removed(observe: Observe<Pair<Removed, Cheese>>) +{ + for evt_match in &observe { + let cheese = evt_match.get_ent_target_comp(); + + println!("{} cheese was eaten", cheese.name); + } +} + +fn on_cheese_crumbs_changed(observe: Observe<Pair<Changed, CheeseCrumbs>>) +{ + for evt_match in &observe { + let cheese_crumbs = evt_match.get_ent_target_comp(); + + println!("Cheese crumbs count changed to {}", cheese_crumbs.cnt); + } +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*UPDATE, eat_cheese); + world.register_observer(on_cheese_removed); + world.register_observer(on_cheese_crumbs_changed); + + world.create_entity((Cheese { name: "Brie" }, CheeseCrumbs { cnt: 0 })); + world.create_entity((Cheese { name: "Parmesan" }, CheeseCrumbs { cnt: 0 })); + world.create_entity((Cheese { name: "Gouda" }, CheeseCrumbs { cnt: 0 })); + + world.step(); +} diff --git a/engine-ecs/examples/component_relationship.rs b/engine-ecs/examples/component_relationship.rs new file mode 100644 index 0000000..0f7b514 --- /dev/null +++ b/engine-ecs/examples/component_relationship.rs @@ -0,0 +1,65 @@ +use engine_ecs::pair::Pair; +use engine_ecs::phase::START as START_PHASE; +use engine_ecs::{Component, Query, World}; + +#[derive(Component)] +struct Person +{ + name: String, +} + +fn print_dog_likers(query: Query<(&Person, Pair<Likes, &Dogs>)>) +{ + for (person, liked_dogs) in &query { + println!( + "{} likes {} dogs!", + person.name, + if liked_dogs.large { "large" } else { "small" }, + ); + } +} + +#[derive(Component)] +struct Likes; + +#[derive(Component)] +struct Cats; + +#[derive(Component)] +struct Dogs +{ + large: bool, +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*START_PHASE, print_dog_likers); + + world.create_entity(( + Person { name: "Irving".to_string() }, + Pair::builder() + .relation::<Likes>() + .target_as_data(Dogs { large: true }) + .build(), + )); + + world.create_entity(( + Person { name: "Mark".to_string() }, + Pair::builder() + .relation::<Likes>() + .target_as_data(Cats) + .build(), + )); + + world.create_entity(( + Person { name: "Helena".to_string() }, + Pair::builder() + .relation::<Likes>() + .target_as_data(Dogs { large: false }) + .build(), + )); + + world.step(); +} diff --git a/engine-ecs/examples/component_removed_event.rs b/engine-ecs/examples/component_removed_event.rs new file mode 100644 index 0000000..b15c2c3 --- /dev/null +++ b/engine-ecs/examples/component_removed_event.rs @@ -0,0 +1,46 @@ +use engine_ecs::actions::Actions; +use engine_ecs::component::Component; +use engine_ecs::event::component::{EventMatchExt, Removed}; +use engine_ecs::pair::Pair; +use engine_ecs::phase::UPDATE; +use engine_ecs::system::observer::Observe; +use engine_ecs::{Component, Query, World}; + +#[derive(Debug, Component)] +struct Cheese +{ + name: &'static str, +} + +fn eat_cheese(query: Query<(&Cheese,)>, mut actions: Actions) +{ + for (cheese_ent_id, (_,)) in query.iter_with_euids() { + println!("Eating cheese!"); + + actions.remove_components(cheese_ent_id, [Cheese::id()]); + } +} + +fn on_cheese_removed(observe: Observe<Pair<Removed, Cheese>>) +{ + for evt_match in &observe { + let cheese = evt_match.get_ent_target_comp(); + + println!("{} cheese was eaten", cheese.name); + } +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*UPDATE, eat_cheese); + world.register_observer(on_cheese_removed); + + world.create_entity((Cheese { name: "Brie" },)); + world.create_entity((Cheese { name: "Parmesan" },)); + world.create_entity((Cheese { name: "Gouda" },)); + + world.step(); + world.step(); +} diff --git a/engine-ecs/examples/error_handling.rs b/engine-ecs/examples/error_handling.rs new file mode 100644 index 0000000..3d57778 --- /dev/null +++ b/engine-ecs/examples/error_handling.rs @@ -0,0 +1,79 @@ +use engine_ecs::error::Error; +use engine_ecs::event::component::{Changed, EventMatchExt}; +use engine_ecs::pair::Pair; +use engine_ecs::phase::UPDATE; +use engine_ecs::query::Query; +use engine_ecs::system::observer::Observe; +use engine_ecs::{Component, World, error}; +use tracing::level_filters::LevelFilter; +use tracing_subscriber::EnvFilter; +use tracing_subscriber::fmt::time::ChronoLocal; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; + +#[derive(Component)] +struct State +{ + value: u32, +} + +fn do_something_fallible(query: Query<(&mut State,)>) -> Result<(), Error> +{ + for (mut state,) in &query { + state.value += 1; + + state.set_changed(); + + if state.value > 3 { + return Err(error!("Invalid state value {}", state.value)); + } + } + + Ok(()) +} + +fn handle_state_changed(observe: Observe<Pair<Changed, State>>) -> Result<(), Error> +{ + for evt_match in &observe { + let state = evt_match.get_ent_target_comp(); + + if state.value > 3 { + return Err(error!("Invalid state value {}", state.value)); + } + + tracing::info!("State has valid value {}", state.value); + } + + Ok(()) +} + +fn main() +{ + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .with_timer(ChronoLocal::new("%T%.6f".to_string())), + ) + .with( + EnvFilter::builder() + .with_default_directive(LevelFilter::DEBUG.into()) + .from_env() + .unwrap(), + ) + .init(); + + let mut world = World::new(); + + world.set_err_handler(engine_ecs::error::err_handler_log_error); + + world.create_entity((State { value: 0 },)); + + world.register_system(*UPDATE, do_something_fallible); + + world.register_observer(handle_state_changed); + + world.step(); + world.step(); + world.step(); + world.step(); +} diff --git a/engine-ecs/examples/event_loop.rs b/engine-ecs/examples/event_loop.rs new file mode 100644 index 0000000..62d0876 --- /dev/null +++ b/engine-ecs/examples/event_loop.rs @@ -0,0 +1,120 @@ +use engine_ecs::actions::Actions; +use engine_ecs::pair::{ChildOf, Pair}; +use engine_ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; +use engine_ecs::{declare_entity, Component, Query, World}; + +#[derive(Component)] +struct Wool +{ + remaining: u32, +} + +#[derive(Component)] +struct Health +{ + health: u32, +} + +#[derive(Component)] +struct Name +{ + name: &'static str, +} + +fn sheer(query: Query<(&mut Wool, &Name)>) +{ + for (mut wool, name) in &query { + if wool.remaining == 0 { + println!("{} Has no wool left", name.name); + + continue; + } + + // Sheer the whool + wool.remaining -= 5; + + println!("Sheered 5 wool from {}", name.name); + } +} + +fn feed(query: Query<(&mut Health, &Name)>) +{ + for (mut health, name) in &query { + health.health += 1; + + println!("Feeded {} which gained 1 health", name.name); + } +} + +fn age(query: Query<(&mut Health, &Name)>, mut actions: Actions) +{ + for (mut health, name) in &query { + if health.health <= 2 { + health.health = 0; + + println!("{} passed away", name.name); + + actions.stop(); + + continue; + } + + health.health -= 2; + + println!("{} aged and lost 2 health", name.name); + } +} + +declare_entity!( + SHEER_PHASE, + ( + Phase, + Pair::builder() + .relation::<ChildOf>() + .target_id(*UPDATE_PHASE) + .build() + ) +); + +declare_entity!( + FEED_PHASE, + ( + Phase, + Pair::builder() + .relation::<ChildOf>() + .target_id(*SHEER_PHASE) + .build() + ) +); + +declare_entity!( + AGE_PHASE, + ( + Phase, + Pair::builder() + .relation::<ChildOf>() + .target_id(*FEED_PHASE) + .build() + ) +); + +fn main() +{ + let mut world = World::new(); + + world.create_declared_entity(&SHEER_PHASE); + world.create_declared_entity(&FEED_PHASE); + world.create_declared_entity(&AGE_PHASE); + + world.register_system(*SHEER_PHASE, sheer); + world.register_system(*FEED_PHASE, feed); + world.register_system(*AGE_PHASE, age); + + world.create_entity(( + Wool { remaining: 30 }, + Health { health: 3 }, + Name { name: "Bessy" }, + )); + + world.start_loop(); +} diff --git a/engine-ecs/examples/extension.rs b/engine-ecs/examples/extension.rs new file mode 100644 index 0000000..a96c1a7 --- /dev/null +++ b/engine-ecs/examples/extension.rs @@ -0,0 +1,70 @@ +use engine_ecs::actions::Actions; +use engine_ecs::extension::{Collector as ExtensionCollector, Extension}; +use engine_ecs::phase::UPDATE as UPDATE_PHASE; +use engine_ecs::{Component, Query, World}; + +#[derive(Debug, Component)] +struct Position +{ + x: u32, + y: u32, +} + +#[derive(Debug, Component)] +struct EnemySpawnSource; + +#[derive(Debug, Component)] +enum EvilnessLevel +{ + Medium, +} + +fn spawn_enemies( + spawner_query: Query<(&EnemySpawnSource, &Position)>, + enemies_query: Query<(&EvilnessLevel,)>, + mut actions: Actions, +) +{ + let Some((_, enemy_spawner_position)) = spawner_query.iter().next() else { + return; + }; + + let enemy_cnt = enemies_query.iter().count(); + + if enemy_cnt > 3 { + return; + } + + actions.spawn(( + EvilnessLevel::Medium, + Position { + x: enemy_spawner_position.x * enemy_cnt as u32, + y: enemy_spawner_position.y, + }, + )); + + println!("Spawned enemy with medium evilness and 45 strength"); +} + +struct EnemySpawningExtension; + +impl Extension for EnemySpawningExtension +{ + fn collect(self, mut collector: ExtensionCollector<'_>) + { + collector.add_system(*UPDATE_PHASE, spawn_enemies); + + collector.add_entity((Position { x: 187, y: 30 }, EnemySpawnSource)); + } +} + +fn main() +{ + let mut world = World::new(); + + world.add_extension(EnemySpawningExtension); + + for _ in 0..7 { + world.step(); + } +} diff --git a/engine-ecs/examples/multiple_queries.rs b/engine-ecs/examples/multiple_queries.rs new file mode 100644 index 0000000..1a4aaad --- /dev/null +++ b/engine-ecs/examples/multiple_queries.rs @@ -0,0 +1,85 @@ +use std::fmt::Display; + +use engine_ecs::phase::START as START_PHASE; +use engine_ecs::{Component, Query, World}; + +#[derive(Component)] +struct Health +{ + health: u32, +} + +#[derive(Component)] +enum AttackStrength +{ + Strong, + Weak, +} + +#[derive(Component)] +struct EnemyName +{ + name: String, +} + +impl Display for EnemyName +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + self.name.fmt(formatter) + } +} + +fn do_attacks( + attacker_query: Query<(&AttackStrength,)>, + enemy_query: Query<(&mut Health, &EnemyName)>, +) +{ + for (attack_strength,) in &attacker_query { + for (mut health, enemy_name) in &enemy_query { + let damage = match *attack_strength { + AttackStrength::Strong => 20, + AttackStrength::Weak => 10, + }; + + if health.health <= damage { + println!("Enemy '{}' died", *enemy_name); + + health.health = 0; + + continue; + } + + health.health -= damage; + + println!("Enemy '{}' took {damage} damage", *enemy_name); + } + } +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*START_PHASE, do_attacks); + + world.create_entity(( + Health { health: 100 }, + EnemyName { name: "Big spider".to_string() }, + )); + + world.create_entity(( + Health { health: 30 }, + EnemyName { name: "Small goblin".to_string() }, + )); + + world.create_entity(( + Health { health: 30 }, + EnemyName { name: "Headcrab".to_string() }, + )); + + world.create_entity((AttackStrength::Strong,)); + world.create_entity((AttackStrength::Weak,)); + + world.step(); +} diff --git a/engine-ecs/examples/optional_component.rs b/engine-ecs/examples/optional_component.rs new file mode 100644 index 0000000..79650b9 --- /dev/null +++ b/engine-ecs/examples/optional_component.rs @@ -0,0 +1,81 @@ +use engine_ecs::phase::UPDATE as UPDATE_PHASE; +use engine_ecs::{Component, Query, World}; + +#[derive(Debug, Component)] +struct PettingCapacity +{ + capacity_left: u32, +} + +#[derive(Debug, Clone, Copy, Component)] +enum Aggressivity +{ + High, + Medium, + Low, +} + +#[derive(Debug, Component)] +pub struct CatName +{ + name: String, +} + +fn pet_cats(query: Query<(&CatName, &mut PettingCapacity, Option<&Aggressivity>)>) +{ + for (cat_name, mut petting_capacity, aggressivity) in &query { + let Some(aggressivity) = aggressivity else { + println!("Aggressivity of cat {} is unknown. Skipping", cat_name.name); + continue; + }; + + if let Aggressivity::High = *aggressivity { + println!("Cat {} is aggressive. Skipping", cat_name.name); + continue; + } + + if petting_capacity.capacity_left == 0 { + println!( + "Cat {} have had enough of being petted. Skipping", + cat_name.name + ); + continue; + } + + println!("Petting cat {}", cat_name.name); + + petting_capacity.capacity_left -= 1; + } +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*UPDATE_PHASE, pet_cats); + + world.create_entity(( + CatName { name: "Jasper".to_string() }, + Aggressivity::Medium, + PettingCapacity { capacity_left: 5 }, + )); + + world.create_entity(( + CatName { name: "Otto".to_string() }, + PettingCapacity { capacity_left: 9 }, + )); + + world.create_entity(( + CatName { name: "Carrie".to_string() }, + PettingCapacity { capacity_left: 2 }, + Aggressivity::High, + )); + + world.create_entity(( + CatName { name: "Tommy".to_string() }, + PettingCapacity { capacity_left: 1 }, + Aggressivity::Low, + )); + + world.step(); +} diff --git a/engine-ecs/examples/relationship.rs b/engine-ecs/examples/relationship.rs new file mode 100644 index 0000000..749c202 --- /dev/null +++ b/engine-ecs/examples/relationship.rs @@ -0,0 +1,56 @@ +use engine_ecs::pair::{Pair, Wildcard}; +use engine_ecs::phase::START as START_PHASE; +use engine_ecs::{Component, Query, World}; + +#[derive(Component)] +struct Sword +{ + attack_strength: u32, +} + +#[derive(Component)] +struct Player; + +#[derive(Component)] +struct Health +{ + health: u32, +} + +#[derive(Component)] +struct Holding; + +fn print_player_stats(player_query: Query<(&Player, &Health, Pair<Holding, Wildcard>)>) +{ + for (_, health, target_sword) in &player_query { + println!("Player health: {}", health.health); + + if let Some(sword_ent) = target_sword.get_target_ent() { + let sword = sword_ent + .get::<Sword>() + .expect("Sword entity is missing sword component"); + + println!("Player sword attack strength: {}", sword.attack_strength); + } + } +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*START_PHASE, print_player_stats); + + let sword_uid = world.create_entity((Sword { attack_strength: 17 },)); + + world.create_entity(( + Player, + Health { health: 180 }, + Pair::builder() + .relation::<Holding>() + .target_id(sword_uid) + .build(), + )); + + world.step(); +} diff --git a/engine-ecs/examples/simple.rs b/engine-ecs/examples/simple.rs new file mode 100644 index 0000000..e03c003 --- /dev/null +++ b/engine-ecs/examples/simple.rs @@ -0,0 +1,42 @@ +use engine_ecs::phase::START as START_PHASE; +use engine_ecs::{Component, Query, World}; + +#[derive(Component)] +struct SomeData +{ + num: u64, +} + +#[derive(Component)] +struct Greeting +{ + greeting: String, +} + +fn say_hello(query: Query<(&SomeData, &Greeting)>) +{ + for (data, greeting) in &query { + println!("{}: {}", greeting.greeting, data.num); + } +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*START_PHASE, say_hello); + + world.create_entity(( + SomeData { num: 987_654 }, + Greeting { + greeting: "Good afternoon".to_string(), + }, + )); + + world.create_entity(( + SomeData { num: 345 }, + Greeting { greeting: "Good evening".to_string() }, + )); + + world.step(); +} diff --git a/engine-ecs/examples/with_local.rs b/engine-ecs/examples/with_local.rs new file mode 100644 index 0000000..2c04f26 --- /dev/null +++ b/engine-ecs/examples/with_local.rs @@ -0,0 +1,70 @@ +use engine_ecs::component::local::Local; +use engine_ecs::phase::UPDATE as UPDATE_PHASE; +use engine_ecs::system::initializable::Initializable; +use engine_ecs::system::Into; +use engine_ecs::{Component, Query, World}; + +#[derive(Component)] +struct SomeData +{ + num: u64, +} + +#[derive(Component)] +struct Name +{ + name: String, +} + +#[derive(Component)] +struct SayHelloState +{ + cnt: usize, +} + +fn say_hello(query: Query<(&SomeData,)>, mut state: Local<SayHelloState>) +{ + for (data,) in &query { + println!("Hello there. Count {}: {}", state.cnt, data.num); + + state.cnt += 1; + } +} + +fn say_whats_up(query: Query<(&SomeData, &Name)>, mut state: Local<SayHelloState>) +{ + for (data, name) in &query { + println!( + "Whats up, {}. Number is {}. Count {}", + name.name, data.num, state.cnt + ); + + state.cnt += 1; + } +} + +fn main() +{ + let mut world = World::new(); + + world.register_system( + *UPDATE_PHASE, + say_hello + .into_system() + .initialize((SayHelloState { cnt: 0 },)), + ); + + world.register_system( + *UPDATE_PHASE, + say_whats_up + .into_system() + .initialize((SayHelloState { cnt: 0 },)), + ); + + world.create_entity((SomeData { num: 987_654 }, Name { name: "Bob".to_string() })); + + world.create_entity((SomeData { num: 345 },)); + + world.step(); + world.step(); +} diff --git a/engine-ecs/examples/with_sole.rs b/engine-ecs/examples/with_sole.rs new file mode 100644 index 0000000..4b2fa44 --- /dev/null +++ b/engine-ecs/examples/with_sole.rs @@ -0,0 +1,61 @@ +use engine_ecs::pair::{ChildOf, Pair}; +use engine_ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; +use engine_ecs::sole::Single; +use engine_ecs::{declare_entity, Component, Query, Sole, World}; + +#[derive(Component)] +struct Ammo +{ + ammo_left: u32, +} + +#[derive(Sole, Default)] +struct AmmoCounter +{ + counter: u32, +} + +fn count_ammo(query: Query<(&Ammo,)>, mut ammo_counter: Single<AmmoCounter>) +{ + for (ammo,) in &query { + println!("Found {} ammo", ammo.ammo_left); + + ammo_counter.counter += ammo.ammo_left; + } +} + +fn print_total_ammo_count(ammo_counter: Single<AmmoCounter>) +{ + println!("Total ammo count: {}", ammo_counter.counter); + + assert_eq!(ammo_counter.counter, 19); +} + +declare_entity!( + PRINT_AMMO_COUNT_PHASE, + ( + Phase, + Pair::builder() + .relation::<ChildOf>() + .target_id(*UPDATE_PHASE) + .build() + ) +); + +fn main() +{ + let mut world = World::new(); + + world.create_declared_entity(&PRINT_AMMO_COUNT_PHASE); + + world.register_system(*UPDATE_PHASE, count_ammo); + world.register_system(*PRINT_AMMO_COUNT_PHASE, print_total_ammo_count); + + world.create_entity((Ammo { ammo_left: 4 },)); + world.create_entity((Ammo { ammo_left: 7 },)); + world.create_entity((Ammo { ammo_left: 8 },)); + + world.add_sole(AmmoCounter::default()).unwrap(); + + world.step(); +} |
