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}