ecs: change state now moves when an entity moves to a different archetype
This commit is contained in:
parent
31d00ad861
commit
85ec31bb65
@ -133,6 +133,11 @@ impl Archetype {
|
||||
Some(unsafe { NonNull::new_unchecked(state.modified_entities.as_ptr() as *mut bool) })
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn get_type_state_mut(&mut self, ty: TypeId) -> Option<&mut TypeState> {
|
||||
self.state.get_mut(&ty)
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn borrow<T: Component>(&self) {
|
||||
if self
|
||||
@ -229,6 +234,7 @@ impl Archetype {
|
||||
self.entities.len() as u32
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn clear_trackers(&mut self) {
|
||||
for type_state in self.state.values_mut() {
|
||||
type_state.clear_trackers();
|
||||
@ -302,6 +308,9 @@ impl Archetype {
|
||||
removed,
|
||||
ty.layout.size(),
|
||||
);
|
||||
|
||||
let type_state = self.state.get_mut(&ty.id).unwrap();
|
||||
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
|
||||
}
|
||||
}
|
||||
self.len = last;
|
||||
@ -317,7 +326,7 @@ impl Archetype {
|
||||
pub(crate) unsafe fn move_to(
|
||||
&mut self,
|
||||
index: u32,
|
||||
mut f: impl FnMut(*mut u8, TypeId, usize),
|
||||
mut f: impl FnMut(*mut u8, TypeId, usize, bool),
|
||||
) -> Option<u32> {
|
||||
let last = self.len - 1;
|
||||
for ty in &self.types {
|
||||
@ -325,8 +334,9 @@ impl Archetype {
|
||||
.get_dynamic(ty.id, ty.layout.size(), index)
|
||||
.unwrap()
|
||||
.as_ptr();
|
||||
f(moved, ty.id(), ty.layout().size());
|
||||
// TODO: copy component tracker state here
|
||||
let type_state = self.state.get(&ty.id).unwrap();
|
||||
let is_modified = type_state.modified_entities[index as usize];
|
||||
f(moved, ty.id(), ty.layout().size(), is_modified);
|
||||
if index != last {
|
||||
ptr::copy_nonoverlapping(
|
||||
self.get_dynamic(ty.id, ty.layout.size(), last)
|
||||
@ -335,6 +345,8 @@ impl Archetype {
|
||||
moved,
|
||||
ty.layout.size(),
|
||||
);
|
||||
let type_state = self.state.get_mut(&ty.id).unwrap();
|
||||
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
|
||||
}
|
||||
}
|
||||
self.len -= 1;
|
||||
@ -379,10 +391,10 @@ impl Drop for Archetype {
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeState {
|
||||
pub struct TypeState {
|
||||
offset: usize,
|
||||
borrow: AtomicBorrow,
|
||||
modified_entities: Vec<bool>,
|
||||
pub modified_entities: Vec<bool>,
|
||||
}
|
||||
|
||||
impl TypeState {
|
||||
|
||||
@ -386,8 +386,9 @@ impl World {
|
||||
let target_index = target_arch.allocate(entity.id());
|
||||
loc.archetype = target;
|
||||
let old_index = mem::replace(&mut loc.index, target_index);
|
||||
if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size| {
|
||||
if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size, is_modified| {
|
||||
target_arch.put_dynamic(ptr, ty, size, target_index);
|
||||
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified;
|
||||
}) {
|
||||
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
|
||||
}
|
||||
@ -463,10 +464,11 @@ impl World {
|
||||
let target_index = target_arch.allocate(entity.id());
|
||||
loc.archetype = target;
|
||||
loc.index = target_index;
|
||||
if let Some(moved) = source_arch.move_to(old_index, |src, ty, size| {
|
||||
if let Some(moved) = source_arch.move_to(old_index, |src, ty, size, is_modified| {
|
||||
// Only move the components present in the target archetype, i.e. the non-removed ones.
|
||||
if let Some(dst) = target_arch.get_dynamic(ty, size, target_index) {
|
||||
ptr::copy_nonoverlapping(src, dst.as_ptr(), size);
|
||||
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified;
|
||||
}
|
||||
}) {
|
||||
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
|
||||
|
||||
@ -136,7 +136,7 @@ mod tests {
|
||||
fn modified_trackers() {
|
||||
let mut world = World::default();
|
||||
let e1 = world.spawn((A(0), B));
|
||||
world.spawn((A(0), B));
|
||||
let e2 = world.spawn((A(0), B));
|
||||
let e3 = world.spawn((A(0), B));
|
||||
world.spawn((A(0), B));
|
||||
|
||||
@ -146,20 +146,31 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let changed_entities = world
|
||||
.query::<Changed<A, Entity>>()
|
||||
.iter()
|
||||
.collect::<Vec<Entity>>();
|
||||
assert_eq!(changed_entities, vec![e1, e3]);
|
||||
fn get_changed(world: &World) -> Vec<Entity> {
|
||||
world
|
||||
.query::<Changed<A, Entity>>()
|
||||
.iter()
|
||||
.collect::<Vec<Entity>>()
|
||||
};
|
||||
|
||||
assert_eq!(get_changed(&world), vec![e1, e3]);
|
||||
|
||||
// ensure changing an entity's archetypes also moves its modified state
|
||||
world.insert(e1, (C,)).unwrap();
|
||||
|
||||
let changed_entities = world
|
||||
.query::<Changed<A, Entity>>()
|
||||
.iter()
|
||||
.collect::<Vec<Entity>>();
|
||||
assert_eq!(changed_entities, vec![e1, e3]);
|
||||
|
||||
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
|
||||
|
||||
// spawning a new A entity should not change existing modified state
|
||||
world.insert(e1, (A(0), B)).unwrap();
|
||||
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change");
|
||||
|
||||
// removing an unchanged entity should not change modified state
|
||||
world.despawn(e2).unwrap();
|
||||
assert_eq!(get_changed(&world), vec![e3, e1], "changed entities list should not change");
|
||||
|
||||
// removing a changed entity should remove it from enumeration
|
||||
world.despawn(e1).unwrap();
|
||||
assert_eq!(get_changed(&world), vec![e3], "e1 should no longer be returned");
|
||||
|
||||
world.clear_trackers();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user