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