pub trait Join {
type Type;
type Value;
type Mask: BitSetLike;
unsafe fn open(self) -> (Self::Mask, Self::Value);
unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type;
fn join(self) -> JoinIter<Self>ⓘNotable traits for JoinIter<J>impl<J: Join> Iterator for JoinIter<J> type Item = J::Type;
where
Self: Sized,
{ ... }
fn maybe(self) -> MaybeJoin<Self>
where
Self: Sized,
{ ... }
fn is_unconstrained() -> bool { ... }
}
Expand description
The purpose of the Join
trait is to provide a way
to access multiple storages at the same time with
the merged bit set.
Joining component storages means that you’ll only get values where for a given entity every storage has an associated component.
Example
let mut world = World::new();
world.register::<Pos>();
world.register::<Vel>();
{
let pos = world.read_storage::<Pos>();
let vel = world.read_storage::<Vel>();
// There are no entities yet, so no pair will be returned.
let joined: Vec<_> = (&pos, &vel).join().collect();
assert_eq!(joined, vec![]);
}
world
.create_entity()
.with(Pos)
.build();
{
let pos = world.read_storage::<Pos>();
let vel = world.read_storage::<Vel>();
// Although there is an entity, it only has `Pos`.
let joined: Vec<_> = (&pos, &vel).join().collect();
assert_eq!(joined, vec![]);
}
let ent = world.create_entity()
.with(Pos)
.with(Vel)
.build();
{
let pos = world.read_storage::<Pos>();
let vel = world.read_storage::<Vel>();
// Now there is one entity that has both a `Vel` and a `Pos`.
let joined: Vec<_> = (&pos, &vel).join().collect();
assert_eq!(joined, vec![(&Pos, &Vel)]);
// If we want to get the entity the components are associated to,
// we need to join over `Entities`:
let entities = world.read_resource::<EntitiesRes>();
// note: `EntitiesRes` is the fetched resource; we get back
// `Read<EntitiesRes>`.
// `Read<EntitiesRes>` can also be referred to by `Entities` which
// is a shorthand type definition to the former type.
let joined: Vec<_> = (&entities, &pos, &vel).join().collect();
assert_eq!(joined, vec![(ent, &Pos, &Vel)]);
}
Iterating over a single storage
Join
can also be used to iterate over a single
storage, just by writing (&storage).join()
.
Required Associated Types
sourcetype Mask: BitSetLike
type Mask: BitSetLike
Type of joined bit mask.
Required Methods
sourceunsafe fn open(self) -> (Self::Mask, Self::Value)
unsafe fn open(self) -> (Self::Mask, Self::Value)
Open this join by returning the mask and the storages.
This is unsafe because implementations of this trait can permit
the Value
to be mutated independently of the Mask
.
If the Mask
does not correctly report the status of the Value
then illegal memory access can occur.
Provided Methods
sourcefn join(self) -> JoinIter<Self>ⓘNotable traits for JoinIter<J>impl<J: Join> Iterator for JoinIter<J> type Item = J::Type;
where
Self: Sized,
fn join(self) -> JoinIter<Self>ⓘNotable traits for JoinIter<J>impl<J: Join> Iterator for JoinIter<J> type Item = J::Type;
where
Self: Sized,
Create a joined iterator over the contents.
sourcefn maybe(self) -> MaybeJoin<Self>where
Self: Sized,
fn maybe(self) -> MaybeJoin<Self>where
Self: Sized,
Returns a Join
-able structure that yields all indices, returning None
for all
missing elements and Some(T)
for found elements.
WARNING: Do not have a join of only MaybeJoin
s. Otherwise the join will
iterate over every single index of the bitset. If you want a join with
all MaybeJoin
s, add an EntitiesRes
to the join as well to bound the
join to all entities that are alive.
struct ExampleSystem;
impl<'a> System<'a> for ExampleSystem {
type SystemData = (
WriteStorage<'a, Pos>,
ReadStorage<'a, Vel>,
);
fn run(&mut self, (mut positions, velocities): Self::SystemData) {
for (mut position, maybe_velocity) in (&mut positions, velocities.maybe()).join() {
if let Some(velocity) = maybe_velocity {
position.x += velocity.x;
position.y += velocity.y;
}
}
}
}
fn main() {
let mut world = World::new();
let mut dispatcher = DispatcherBuilder::new()
.with(ExampleSystem, "example_system", &[])
.build();
dispatcher.setup(&mut world.res);
let e1 = world.create_entity()
.with(Pos { x: 0, y: 0 })
.with(Vel { x: 5, y: 2 })
.build();
let e2 = world.create_entity()
.with(Pos { x: 0, y: 0 })
.build();
dispatcher.dispatch(&mut world.res);
let positions = world.read_storage::<Pos>();
assert_eq!(positions.get(e1), Some(&Pos { x: 5, y: 2 }));
assert_eq!(positions.get(e2), Some(&Pos { x: 0, y: 0 }));
}
sourcefn is_unconstrained() -> bool
fn is_unconstrained() -> bool
If this Join
typically returns all indices in the mask, then iterating over only it
or combined with other joins that are also dangerous will cause the JoinIter
/ParJoin
to
go through all indices which is usually not what is wanted and will kill performance.