-
Notifications
You must be signed in to change notification settings - Fork 221
/
full.rs
273 lines (223 loc) · 7.55 KB
/
full.rs
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
use specs::{prelude::*, shred, storage::HashMapStorage};
// -- Components --
// A component exists for 0..n
// entities.
#[derive(Clone, Debug)]
struct CompInt(i32);
impl Component for CompInt {
// Storage is used to store all data for components of this type
// VecStorage is meant to be used for components that are in almost every entity
type Storage = VecStorage<Self>;
}
#[derive(Clone, Debug)]
struct CompBool(bool);
impl Component for CompBool {
// HashMapStorage is better for components that are met rarely
type Storage = HashMapStorage<Self>;
}
#[derive(Clone, Debug)]
struct CompFloat(f32);
impl Component for CompFloat {
type Storage = DenseVecStorage<Self>;
}
// -- Resources --
// Resources are unique and can be accessed
// from systems using the same sync strategy
// as component storages
#[derive(Clone, Debug)]
struct Sum(usize);
// -- System Data --
// Each system has an associated
// data type.
#[derive(SystemData)]
struct IntAndBoolData<'a> {
comp_int: ReadStorage<'a, CompInt>,
comp_bool: WriteStorage<'a, CompBool>,
}
#[derive(SystemData)]
struct SpawnData<'a> {
comp_int: WriteStorage<'a, CompInt>,
entities: Entities<'a>,
}
#[derive(SystemData)]
struct StoreMaxData<'a> {
comp_float: ReadStorage<'a, CompFloat>,
comp_int: ReadStorage<'a, CompInt>,
entities: Entities<'a>,
}
// -- Systems --
struct SysPrintBool;
impl<'a> System<'a> for SysPrintBool {
type SystemData = ReadStorage<'a, CompBool>;
fn run(&mut self, data: ReadStorage<CompBool>) {
for b in (&data).join() {
println!("Bool: {:?}", b);
}
}
}
struct SysCheckPositive;
impl<'a> System<'a> for SysCheckPositive {
type SystemData = IntAndBoolData<'a>;
fn run(&mut self, mut data: IntAndBoolData) {
// Join merges the two component storages,
// so you get all (CompInt, CompBool) pairs.
for (ci, cb) in (&data.comp_int, &mut data.comp_bool).join() {
cb.0 = ci.0 > 0;
}
}
}
struct SysSpawn {
counter: i32,
}
impl SysSpawn {
fn new() -> Self {
Self { counter: 0 }
}
}
impl<'a> System<'a> for SysSpawn {
type SystemData = SpawnData<'a>;
fn run(&mut self, mut data: SpawnData) {
if self.counter == 0 {
let entity = data.entities.join().next().unwrap();
let _ = data.entities.delete(entity);
}
let entity = data.entities.create();
// This line can't fail because we just made the entity.
data.comp_int.insert(entity, CompInt(self.counter)).unwrap();
self.counter += 1;
if self.counter > 100 {
self.counter = 0;
}
}
}
/// Stores the entity with
/// the greatest int.
struct SysStoreMax(Option<Entity>);
impl SysStoreMax {
fn new() -> Self {
Self(None)
}
}
impl<'a> System<'a> for SysStoreMax {
type SystemData = StoreMaxData<'a>;
fn run(&mut self, data: StoreMaxData) {
use std::i32::MIN;
// Let's print information about
// last run's entity
if let Some(e) = self.0 {
if let Some(f) = data.comp_float.get(e) {
println!("Entity with biggest int has float value {:?}", f);
} else {
println!("Entity with biggest int has no float value");
}
}
let mut max_entity = None;
let mut max = MIN;
for (entity, value) in (&data.entities, &data.comp_int).join() {
if value.0 >= max {
max = value.0;
max_entity = Some(entity);
}
}
self.0 = max_entity;
}
}
#[cfg(feature = "parallel")]
struct JoinParallel;
#[cfg(feature = "parallel")]
impl<'a> System<'a> for JoinParallel {
type SystemData = (
ReadStorage<'a, CompBool>,
ReadStorage<'a, CompInt>,
WriteStorage<'a, CompFloat>,
);
fn run(&mut self, (comp_bool, comp_int, mut comp_float): Self::SystemData) {
use specs::rayon::prelude::*;
(&comp_bool, &comp_int, &mut comp_float)
.par_join()
// only iterate over entities with a `CompBool(true)`
.filter(|&(b, _, _)| b.0)
// set the `CompFloat` value to the float repr of `CompInt`
.for_each(|(_, i, f)| f.0 += i.0 as f32);
}
}
/// Takes every `CompFloat` and tries to add `CompInt` if it exists.
struct AddIntToFloat;
impl<'a> System<'a> for AddIntToFloat {
type SystemData = (ReadStorage<'a, CompInt>, ReadStorage<'a, CompFloat>);
fn run(&mut self, (comp_int, comp_float): Self::SystemData) {
// This system demonstrates the use of `.maybe()`.
// As the name implies, it doesn't filter any entities; it yields an
// `Option<CompInt>`. So the `join` will yield all entities that have a
// `CompFloat`, just returning a `CompInt` if the entity happens to have
// one.
for (f, i) in (&comp_float, comp_int.maybe()).join() {
let sum = f.0 + i.map(|i| i.0 as f32).unwrap_or(0.0);
println!("Result: sum = {}", sum);
}
// An alternative way to write this out:
// (note that `entities` is just another system data of type
// `Entities<'a>`)
//
// ```
// for (entity, f) in (&entities, &comp_float).join() {
// let i = comp_int.get(e); // retrieves the component for the current entity
//
// let sum = f.0 + i.map(|i| i.0 as f32).unwrap_or(0.0);
// println!("Result: sum = {}", sum);
// }
// ```
}
}
fn main() {
let mut w = World::new();
// This builds our dispatcher, which contains the systems.
// Every system has a name and can depend on other systems.
// "check_positive" depends on "print_bool" for example,
// because we want to print the components before executing
// `SysCheckPositive`.
let mut dispatcher_builder = DispatcherBuilder::new()
.with(SysPrintBool, "print_bool", &[])
.with(SysCheckPositive, "check_positive", &["print_bool"])
.with(SysStoreMax::new(), "store_max", &["check_positive"])
.with(SysSpawn::new(), "spawn", &[])
.with(SysPrintBool, "print_bool2", &["check_positive"]);
#[cfg(feature = "parallel")]
{
dispatcher_builder = dispatcher_builder.with(JoinParallel, "join_par", &[])
}
let mut dispatcher = dispatcher_builder
.with_barrier() // we want to make sure all systems finished before running the last one
.with(AddIntToFloat, "add_float_int", &[])
.build();
// setup() will setup all Systems we added, registering all resources and
// components that they will try to read from and write to
dispatcher.setup(&mut w);
// create_entity() of World provides with an EntityBuilder to add components to
// an Entity
w.create_entity()
.with(CompInt(4))
.with(CompBool(false))
.build();
// build() returns an entity, we will use it later to perform a deletion
let e = w
.create_entity()
.with(CompInt(9))
.with(CompBool(true))
.build();
w.create_entity()
.with(CompInt(-1))
.with(CompBool(false))
.build();
w.create_entity().with(CompInt(127)).build();
w.create_entity().with(CompBool(false)).build();
w.create_entity().with(CompFloat(0.1)).build();
dispatcher.dispatch(&w);
w.maintain();
// Insert a component, associated with `e`.
if let Err(err) = w.write_storage().insert(e, CompFloat(4.0)) {
eprintln!("Failed to insert component! {:?}", err);
}
dispatcher.dispatch(&w);
w.maintain();
}