summaryrefslogtreecommitdiff
path: root/engine-ecs/examples
diff options
context:
space:
mode:
Diffstat (limited to 'engine-ecs/examples')
-rw-r--r--engine-ecs/examples/component_changed_event.rs78
-rw-r--r--engine-ecs/examples/component_events.rs64
-rw-r--r--engine-ecs/examples/component_relationship.rs65
-rw-r--r--engine-ecs/examples/component_removed_event.rs46
-rw-r--r--engine-ecs/examples/error_handling.rs79
-rw-r--r--engine-ecs/examples/event_loop.rs120
-rw-r--r--engine-ecs/examples/extension.rs70
-rw-r--r--engine-ecs/examples/multiple_queries.rs85
-rw-r--r--engine-ecs/examples/optional_component.rs81
-rw-r--r--engine-ecs/examples/relationship.rs56
-rw-r--r--engine-ecs/examples/simple.rs42
-rw-r--r--engine-ecs/examples/with_local.rs70
-rw-r--r--engine-ecs/examples/with_sole.rs61
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();
+}