Skip to main content

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((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.entity().add(Warrior).add((Platoon, platoon));
76            world.entity().add(Marksman).add((Platoon, platoon));
77            world.entity().add(Wizard).add((Platoon, platoon));
78        }
79    }
80
81    // Create a query to find all RangedUnits for a platoon/player. The
82    // equivalent query in the query DSL would look like this:
83    //   (Platoon, $Platoon), Player($Platoon, $Player)
84    //
85    // The way to read how this query is evaluated is:
86    // - find all entities with (Platoon, *), store * in _Platoon
87    // - check if _Platoon has (Player, *), store * in _Player
88    let mut query = world
89        .query::<()>()
90        .with(RangedUnit)
91        .with(&Platoon)
92        .set_second("$platoon")
93        .with((Player, "$player"))
94        .set_src("$platoon")
95        .build();
96
97    // If we would iterate this query it would return all ranged units for all
98    // platoons & for all players. We can limit the results to just a single
99    // platoon or a single player setting a variable beforehand. In this example
100    // we'll just find all platoons & ranged units for a single player.
101
102    let player_var = query.find_var("player").unwrap();
103    let platoon_var = query.find_var("platoon").unwrap();
104
105    // Iterate query, limit the results to units of MyPlayer
106    query
107        .set_var(player_var, world.lookup_recursive("MyPlayer"))
108        .each_iter(|it, index, _| {
109            let unit = it.entity(index);
110            println!(
111                "Unit id: {} of class {} in platoon id: {} for player {}",
112                unit,
113                it.id(0).to_str(),
114                it.get_var(platoon_var),
115                it.get_var(player_var)
116            );
117        });
118
119    // Output:
120    //  Unit id: 529 of class Wizard in platoon id: 526 for player MyPlayer
121    //  Unit id: 533 of class Wizard in platoon id: 530 for player MyPlayer
122    //  Unit id: 537 of class Wizard in platoon id: 534 for player MyPlayer
123    //  Unit id: 528 of class Marksman in platoon id: 526 for player MyPlayer
124    //  Unit id: 532 of class Marksman in platoon id: 530 for player MyPlayer
125    //  Unit id: 536 of class Marksman in platoon id: 534 for player MyPlayer
126
127    // Try removing the set_var call, this will cause the iterator to return
128    // all units in all platoons for all players.
129}
130
131#[cfg(feature = "flecs_nightly_tests")]
132#[test]
133fn test() {
134    let output_capture = OutputCapture::capture().unwrap();
135    main();
136    output_capture.test("query_setting_variables".to_string());
137}