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}