examples/queries/query_setting_variables.rs
1use crate::z_ignore_test_common::*;
2
3use flecs_ecs::prelude::*;
4// This example extends the component_inheritance example, and shows how
5// we can use a single query to match units from different players and platoons
6// by setting query variables before we iterate.
7//
8// The units in this example belong to a platoon, with the platoons belonging
9// to a player.
10
11// unit datamodel
12#[derive(Component)]
13struct Unit;
14
15#[derive(Component)]
16struct CombatUnit;
17
18#[derive(Component)]
19struct MeleeUnit;
20
21#[derive(Component)]
22struct RangedUnit;
23
24#[derive(Component)]
25struct Warrior;
26
27#[derive(Component)]
28struct Wizard;
29
30#[derive(Component)]
31struct Marksman;
32
33#[derive(Component)]
34struct Player;
35
36#[derive(Component)]
37struct Platoon;
38
39const PLAYER_COUNT: usize = 100;
40const PLATOONS_PER_PLAYER: usize = 3;
41
42fn main() {
43 let world = World::new();
44
45 // Make the ECS aware of the inheritance relationships. Note that IsA
46 // relationship used here is the same as in the prefab example.
47 world.component::<CombatUnit>().is_a::<Unit>();
48 world.component::<MeleeUnit>().is_a::<CombatUnit>();
49 world.component::<RangedUnit>().is_a::<CombatUnit>();
50
51 world.component::<Warrior>().is_a::<MeleeUnit>();
52 world.component::<Wizard>().is_a::<RangedUnit>();
53 world.component::<Marksman>().is_a::<RangedUnit>();
54
55 // Populate store with players and platoons
56 for p in 0..PLAYER_COUNT {
57 let player = if p == 0 {
58 // Give first player a name so we can look it up later
59 world.entity_named("MyPlayer")
60 } else {
61 world.entity()
62 };
63
64 // Add player tag so we can query for all players if we want to
65 player.add::<Player>();
66
67 for _ in 0..PLATOONS_PER_PLAYER {
68 let platoon = world
69 .entity()
70 .add_first::<Player>(player)
71 // Add platoon tag so we can query for all platoons if we want to
72 .add::<Platoon>();
73
74 // Add warriors, wizards and marksmen to the platoon
75 world
76 .entity()
77 .add::<Warrior>()
78 .add_first::<Platoon>(platoon);
79 world
80 .entity()
81 .add::<Marksman>()
82 .add_first::<Platoon>(platoon);
83 world.entity().add::<Wizard>().add_first::<Platoon>(platoon);
84 }
85 }
86
87 // Create a query to find all RangedUnits for a platoon/player. The
88 // equivalent query in the query DSL would look like this:
89 // (Platoon, $Platoon), Player($Platoon, $Player)
90 //
91 // The way to read how this query is evaluated is:
92 // - find all entities with (Platoon, *), store * in _Platoon
93 // - check if _Platoon has (Player, *), store * in _Player
94 let mut query = world
95 .query::<&RangedUnit>()
96 .with::<&Platoon>()
97 .set_second_name("$platoon")
98 .with_first_name::<&Player>("$player")
99 .set_src_name("$platoon")
100 .build();
101
102 // If we would iterate this query it would return all ranged units for all
103 // platoons & for all players. We can limit the results to just a single
104 // platoon or a single player setting a variable beforehand. In this example
105 // we'll just find all platoons & ranged units for a single player.
106
107 let player_var = query.find_var("player").unwrap();
108 let platoon_var = query.find_var("platoon").unwrap();
109
110 // Iterate query, limit the results to units of MyPlayer
111 query
112 .set_var(player_var, world.lookup_recursive("MyPlayer"))
113 .each_iter(|it, index, _| {
114 let unit = it.entity(index);
115 println!(
116 "Unit id: {} of class {} in platoon id: {} for player {}",
117 unit,
118 it.id(0).to_str(),
119 it.get_var(platoon_var),
120 it.get_var(player_var)
121 );
122 });
123
124 // Output:
125 // Unit id: 529 of class Wizard in platoon id: 526 for player MyPlayer
126 // Unit id: 533 of class Wizard in platoon id: 530 for player MyPlayer
127 // Unit id: 537 of class Wizard in platoon id: 534 for player MyPlayer
128 // Unit id: 528 of class Marksman in platoon id: 526 for player MyPlayer
129 // Unit id: 532 of class Marksman in platoon id: 530 for player MyPlayer
130 // Unit id: 536 of class Marksman in platoon id: 534 for player MyPlayer
131
132 // Try removing the set_var call, this will cause the iterator to return
133 // all units in all platoons for all players.
134}
135
136#[cfg(feature = "flecs_nightly_tests")]
137#[test]
138fn test() {
139 let output_capture = OutputCapture::capture().unwrap();
140 main();
141 output_capture.test("query_setting_variables".to_string());
142}