-
Notifications
You must be signed in to change notification settings - Fork 220
/
Copy pathslices.rs
107 lines (87 loc) · 3.61 KB
/
slices.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
extern crate specs;
use specs::prelude::*;
// A component contains data which is associated with an entity.
#[derive(Debug)]
struct Vel(f32);
impl Component for Vel {
type Storage = DefaultVecStorage<Self>;
}
impl Default for Vel {
fn default() -> Self {
Self(0.0)
}
}
#[derive(Debug)]
struct Pos(f32);
impl Component for Pos {
type Storage = DefaultVecStorage<Self>;
}
impl Default for Pos {
fn default() -> Self {
Self(0.0)
}
}
struct SysA;
impl<'a> System<'a> for SysA {
// These are the resources required for execution.
// You can also define a struct and `#[derive(SystemData)]`,
// see the `full` example.
type SystemData = (WriteStorage<'a, Pos>, ReadStorage<'a, Vel>);
fn run(&mut self, (mut pos, vel): Self::SystemData) {
// Both the `Pos` and `Vel` components use `DefaultVecStorage`, which supports
// `as_slice()` and `as_mut_slice()`. This lets us access components without
// indirection.
let pos_slice = pos.as_mut_slice();
let vel_slice = vel.as_slice();
// Note that an entity which has position but not velocity will still have
// an entry in both slices. `DefaultVecStorage` is sparse, and here is where
// that matters: the storage has space for many entities, but not all of them
// contain meaningful values. These slices may be a mix of present and absent
// (`Default`) data.
//
// We could check the `mask()` before reading the velocity and updating the
// position, and this is what `.join()` normally does. However, because:
//
// 1. `Vel` uses `DefaultVecStorage`,
// 2. `Vel`'s default is 0.0, and
// 3. `Pos` += 0.0 is a no-op,
//
// we can unconditionally add `Vel` to `Pos` without reading the `mask()`!
// This results in a tight inner loop of known size, which is especially
// suitable for SIMD, OpenCL, CUDA, and other accelerator technologies.
//
// Finally, note that `DefaultVecStorage` and `VecStorage` slice indices
// always agree. If an entity is at location `i` in one `VecStorage`, it
// will be at location `i` in every other `VecStorage`. (By contrast,
// `DenseVecStorage` uses unpredictable indices and cannot be used in
// this way.) We need only worry about handling slices of different
// lengths.
let len = pos_slice.len().min(vel_slice.len());
for i in 0..len {
pos_slice[i].0 += vel_slice[i].0;
}
}
}
fn main() {
// The `World` is our
// container for components
// and other resources.
let mut world = World::new();
// This builds a dispatcher.
// The third parameter of `add` specifies
// logical dependencies on other systems.
// Since we only have one, we don't depend on anything.
// See the `full` example for dependencies.
let mut dispatcher = DispatcherBuilder::new().with(SysA, "sys_a", &[]).build();
// setup() must be called before creating any entity, it will register
// all Components and Resources that Systems depend on
dispatcher.setup(&mut world);
// An entity may or may not contain some component.
world.create_entity().with(Vel(2.0)).with(Pos(0.0)).build();
world.create_entity().with(Vel(4.0)).with(Pos(1.6)).build();
world.create_entity().with(Vel(1.5)).with(Pos(5.4)).build();
// This entity does not have `Vel`, so it won't be dispatched.
world.create_entity().with(Pos(2.0)).build();
// This dispatches all the systems in parallel (but blocking).
dispatcher.dispatch(&world);
}