Skip to main content

entities/
entities.rs

1use sceller::prelude::*;
2
3#[derive(Debug)]
4struct Health(u32);
5#[derive(Debug)]
6struct Speed(u32);
7
8struct Enemy;
9
10fn main() -> Result<()> {
11    let mut world = World::new();
12
13    // insert_checked will return an error if the insertion of a new component fails.
14    world.spawn()
15        .insert_checked(Health(12))?
16        .insert_checked(Speed(15))?        
17        .insert(Enemy);
18
19    // on the other hand, insert will panic if there is an error. (which is unlikely)
20    world.spawn()
21        .insert(Health(12093))
22        .insert(12_u32)
23        .insert(Enemy);
24
25    // Any struct or even primitive that implements the 'Any' trait (so basically everything) can 
26    // be a component. As you can see, in our second entity I inserted a u32.
27
28    // This means that when querying for these entities, you will need to use types:
29
30    // This is an old fashioned query, meaning it returns a vector of vectors of components
31    let query = world.query()
32        .with_component_checked::<Health>()?
33        .with_component_checked::<u32>()?
34        .run();
35
36    // Note that using 'with_component_checked' isn't neccessary, I'm just using it here for the sake
37    // of the example. It can help to explain the problem if something goes wrong. 
38
39    // We can be sure that this query will contain 1 components of type Health, and 1 of type u32:
40    assert_eq!(query[0].len(), 1);
41    assert_eq!(query[1].len(), 1);
42    println!("Asserted that the query contains one item of type 'Health' and one of type 'u32'");
43    // This is because the query filtered for entities with both u32 AND Health.
44    // Note, queries are laid out in the order they are run in, so this query is laid out: [Health, u32]
45
46    { // placed in a block so that this borrow ends after
47        // We know the Healths are in first place.
48        let healths = &query[0];
49
50        // we can retrieve the values by doing this:
51        let borrow = healths[0].borrow();
52        let health1 = borrow.downcast_ref::<Health>().unwrap(); // this is unlikely to fail other than through human error.
53
54        println!("retrieved health struct: {:?}", health1); // print out the health struct 
55    }
56
57    //
58    // IMPORTANT:
59    //
60    // IF YOU EVER GET AN ERROR LIKE THIS: "Expected value of type <...whatever> but found &_"
61    // IT MEANS YOU ARE USING THE WRONG BORROW FUNCTION!!! AND HAVE IMPORTED std::borrow::Borrow!!!!
62    // 
63    // So just remove the import and write borrow bc otherwise this query type will NOT WORK!!!
64    //
65
66    // you can also iterate over them:
67
68    // iterate over components
69    for refcell in &query[0] {
70        let borrow = refcell.borrow();
71        let health = borrow.downcast_ref::<Health>().unwrap();
72        println!("health loop: {:?}", health);
73    }
74
75    // And finally you can borrow them mutably. 
76
77    { // this is in a block to avoid ownership issues and 'stuff'
78        let mut borrow_mut = query[0][0].borrow_mut(); // set the second hp to 50 
79        let mut health = borrow_mut.downcast_mut::<Health>().unwrap();
80
81        // this is the second Health as we queried for Health AND u32 and only the 
82        // second entity has both.
83
84        health.0 = 50;
85    }
86
87    {
88        let new_query = world.query().with_component::<Health>().run(); // all Health components
89
90        let borrow = new_query[0][1].borrow(); // get second Health that we set to 50
91        let first_health=borrow.downcast_ref::<Health>().unwrap();
92
93        assert_eq!(first_health.0, 50);
94        println!("Asserted that the value of the first Health struct was changed to 50");
95    }
96
97    // Auto queries are new, and they are just a really speedy way of getting all of *one* component:
98
99    {
100        let query = world.query();
101        let auto = query.auto::<Health>(); // this is now an iterator over every health in the system.
102        
103        println!("All health values: (there are {} items)", auto.len());
104        for health in auto {
105            println!("{:?}", health);
106        }
107    }   
108
109    // we can also use this to test deleting components from entities
110    world.delete_component_from_ent::<Health>(0); // delete health from first entity
111
112    // we can assert there should be 1 health value left in the system:
113
114    {
115        let query = world.query();
116        let auto = query.auto::<Health>(); // this is now an iterator over every health in the system.
117
118        assert_eq!(auto.len(), 1);
119        println!("Asserted that there exists only 1 health component after deleting.");
120    }
121
122    {
123        // AutoQueries can also be mutable:
124        let query = world.query();
125        let auto = query.auto_mut::<Health>(); // this is now an iterator over every health in the system.
126        
127        for mut hp in auto {
128            hp.0 = 50;
129        }
130        // set all health values to 50
131    }
132
133    // Another method of querying is with Query Functions.
134    // These are a little similar to [bevy](https://bevyengine.org/) systems, although a thousand times more
135    // limiting and about a million times less sophisticated.
136
137    // you pass in a function with the query in it's signature to make the query, as so:
138
139    println!("Beginning function queries:");
140
141    let query = world.query();
142    world.run_system(print_healths); // this will execute this function and fill in the query
143
144    // this function works the same for Query Functions taking mutable arguments
145
146    query.query_fn(&change_healths);
147
148    query.query_fn(&print_healths); // Verify that the health values have changed
149    
150    // query functions currently support a tuple field of up to three components:
151
152    world.run_system(print_two); // this also works with a function with a tuple of three components right now (maybe more later)
153
154    // Query Functions as of now can take up to three arguments as queries:
155
156    world.run_system(print_healths_and_speeds); // this also works with a query taking three arguments    
157
158    // this can also be done with query_fn_mut, and both types can be combines into a single function
159
160    Ok(())
161}
162
163fn print_healths_and_speeds(healths: FnQuery<&Health>, speeds: FnQuery<&Speed>) {
164    for health in healths.iter() {
165        println!("{:?}", health);
166    }
167    for health in healths.iter() {
168        println!("{:?}", health);
169    }
170    for speed in speeds.into_iter() {
171        println!("{:?}", speed);
172    }
173}
174
175fn print_healths(healths: FnQuery<&Health>) {
176    for health in healths.into_iter() {
177        println!("{:?}", health);
178    }
179}
180
181fn change_healths(healths: FnQuery<&mut Health>) {
182    for mut health in healths.into_iter() {
183        health.0 += 100;
184    }
185}
186
187fn print_two(query: FnQuery<(&Speed, &Enemy)>) {
188    // support tuple destructuring
189    for (speed, _) in query.iter() {
190        println!("Enemy: {:?}", speed);
191    }
192}