ecs: component removal tracking
This commit is contained in:
parent
141044aae7
commit
f82af10a69
@ -19,7 +19,7 @@ impl Entity {
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn with_id(id: u32) -> Self {
|
||||
pub fn from_id(id: u32) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
|
||||
@ -100,7 +100,7 @@ impl<'a> Fetch<'a> for EntityFetch {
|
||||
unsafe fn next(&mut self) -> Self::Item {
|
||||
let id = self.0.as_ptr();
|
||||
self.0 = NonNull::new_unchecked(id.add(1));
|
||||
Entity::with_id(*id)
|
||||
Entity::from_id(*id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@ use crate::{
|
||||
pub struct World {
|
||||
entities: Entities,
|
||||
index: HashMap<Vec<TypeId>, u32>,
|
||||
removed_components: HashMap<TypeId, Vec<Entity>>,
|
||||
archetypes: Vec<Archetype>,
|
||||
archetype_generation: u64,
|
||||
}
|
||||
@ -56,6 +57,7 @@ impl World {
|
||||
index,
|
||||
archetypes,
|
||||
archetype_generation: 0,
|
||||
removed_components: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,8 +156,16 @@ impl World {
|
||||
/// Destroy an entity and all its components
|
||||
pub fn despawn(&mut self, entity: Entity) -> Result<(), NoSuchEntity> {
|
||||
let loc = self.entities.free(entity)?;
|
||||
if let Some(moved) = unsafe { self.archetypes[loc.archetype as usize].remove(loc.index) } {
|
||||
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = loc.index;
|
||||
let archetype = &mut self.archetypes[loc.archetype as usize];
|
||||
if let Some(moved) = unsafe { archetype.remove(loc.index) } {
|
||||
self.entities.get_mut(Entity::from_id(moved)).unwrap().index = loc.index;
|
||||
}
|
||||
for ty in archetype.types() {
|
||||
let removed_entities = self
|
||||
.removed_components
|
||||
.entry(ty.id())
|
||||
.or_insert_with(|| Vec::new());
|
||||
removed_entities.push(entity);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -186,8 +196,15 @@ impl World {
|
||||
///
|
||||
/// Preserves allocated storage for reuse.
|
||||
pub fn clear(&mut self) {
|
||||
for x in &mut self.archetypes {
|
||||
x.clear();
|
||||
for archetype in &mut self.archetypes {
|
||||
for ty in archetype.types() {
|
||||
let removed_entities = self
|
||||
.removed_components
|
||||
.entry(ty.id())
|
||||
.or_insert_with(|| Vec::new());
|
||||
removed_entities.extend(archetype.iter_entities().map(|id| Entity::from_id(*id)));
|
||||
}
|
||||
archetype.clear();
|
||||
}
|
||||
self.entities.clear();
|
||||
}
|
||||
@ -317,6 +334,11 @@ impl World {
|
||||
Iter::new(&self.archetypes, &self.entities)
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn removed<C: Component>(&self) -> &[Entity] {
|
||||
self.removed_components.get(&TypeId::of::<C>()).map_or(&[], |entities| entities.as_slice())
|
||||
}
|
||||
|
||||
/// Add `components` to `entity`
|
||||
///
|
||||
/// Computational cost is proportional to the number of components `entity` has. If an entity
|
||||
@ -386,13 +408,15 @@ 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, is_added, is_mutated| {
|
||||
target_arch.put_dynamic(ptr, ty, size, target_index, false);
|
||||
let type_state = target_arch.get_type_state_mut(ty).unwrap();
|
||||
type_state.added_entities[target_index as usize] = is_added;
|
||||
type_state.mutated_entities[target_index as usize] = is_mutated;
|
||||
}) {
|
||||
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
|
||||
if let Some(moved) =
|
||||
source_arch.move_to(old_index, |ptr, ty, size, is_added, is_mutated| {
|
||||
target_arch.put_dynamic(ptr, ty, size, target_index, false);
|
||||
let type_state = target_arch.get_type_state_mut(ty).unwrap();
|
||||
type_state.added_entities[target_index as usize] = is_added;
|
||||
type_state.mutated_entities[target_index as usize] = is_mutated;
|
||||
})
|
||||
{
|
||||
self.entities.get_mut(Entity::from_id(moved)).unwrap().index = old_index;
|
||||
}
|
||||
|
||||
components.put(|ptr, ty, size| {
|
||||
@ -467,16 +491,22 @@ 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, is_added, is_mutated| {
|
||||
// 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);
|
||||
let state = target_arch.get_type_state_mut(ty).unwrap();
|
||||
state.added_entities[target_index as usize] = is_added;
|
||||
state.mutated_entities[target_index as usize] = is_mutated;
|
||||
}
|
||||
}) {
|
||||
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
|
||||
let removed_components = &mut self.removed_components;
|
||||
if let Some(moved) =
|
||||
source_arch.move_to(old_index, |src, ty, size, is_added, is_mutated| {
|
||||
// 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);
|
||||
let state = target_arch.get_type_state_mut(ty).unwrap();
|
||||
state.added_entities[target_index as usize] = is_added;
|
||||
state.mutated_entities[target_index as usize] = is_mutated;
|
||||
} else {
|
||||
let removed_entities = removed_components.entry(ty).or_insert_with(|| Vec::new());
|
||||
removed_entities.push(entity);
|
||||
}
|
||||
})
|
||||
{
|
||||
self.entities.get_mut(Entity::from_id(moved)).unwrap().index = old_index;
|
||||
}
|
||||
Ok(bundle)
|
||||
}
|
||||
@ -572,6 +602,8 @@ impl World {
|
||||
for archetype in self.archetypes.iter_mut() {
|
||||
archetype.clear_trackers();
|
||||
}
|
||||
|
||||
self.removed_components.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -680,7 +712,7 @@ impl<'a> Iterator for Iter<'a> {
|
||||
let index = self.index;
|
||||
self.index += 1;
|
||||
let id = current.entity_id(index);
|
||||
return Some((Entity::with_id(id), unsafe {
|
||||
return Some((Entity::from_id(id), unsafe {
|
||||
EntityRef::new(current, index)
|
||||
}));
|
||||
}
|
||||
|
||||
@ -335,3 +335,32 @@ fn query_one() {
|
||||
world.despawn(a).unwrap();
|
||||
assert!(world.query_one::<&i32>(a).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_tracking() {
|
||||
let mut world = World::new();
|
||||
let a = world.spawn(("abc", 123));
|
||||
let b = world.spawn(("abc", 123));
|
||||
|
||||
world.despawn(a).unwrap();
|
||||
assert_eq!(world.removed::<i32>(), &[a], "despawning results in 'removed component' state");
|
||||
assert_eq!(world.removed::<&'static str>(), &[a], "despawning results in 'removed component' state");
|
||||
|
||||
world.insert_one(b, 10.0).unwrap();
|
||||
assert_eq!(world.removed::<i32>(), &[a], "archetype moves does not result in 'removed component' state");
|
||||
|
||||
world.remove_one::<i32>(b).unwrap();
|
||||
assert_eq!(world.removed::<i32>(), &[a, b], "removing a component results in a 'removed component' state");
|
||||
|
||||
world.clear_trackers();
|
||||
assert_eq!(world.removed::<i32>(), &[], "clearning trackers clears removals");
|
||||
assert_eq!(world.removed::<&'static str>(), &[], "clearning trackers clears removals");
|
||||
assert_eq!(world.removed::<f64>(), &[], "clearning trackers clears removals");
|
||||
|
||||
let c = world.spawn(("abc", 123));
|
||||
let d = world.spawn(("abc", 123));
|
||||
world.clear();
|
||||
assert_eq!(world.removed::<i32>(), &[c, d], "world clears result in 'removed component' states");
|
||||
assert_eq!(world.removed::<&'static str>(), &[c, d, b], "world clears result in 'removed component' states");
|
||||
assert_eq!(world.removed::<f64>(), &[b], "world clears result in 'removed component' states");
|
||||
}
|
||||
@ -200,6 +200,10 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn removed<C: Component>(&self) -> &[Entity] {
|
||||
self.world.removed::<C>()
|
||||
}
|
||||
|
||||
/// Sets the entity's component to the given value. This will fail if the entity does not already have
|
||||
/// the given component type or if the given component type does not match this query.
|
||||
pub fn set<T: Component>(
|
||||
|
||||
@ -20,5 +20,5 @@ fn deserialize_entity(
|
||||
_registry: &PropertyTypeRegistry,
|
||||
) -> Result<Box<dyn Property>, erased_serde::Error> {
|
||||
let entity = private::Entity::deserialize(deserializer)?;
|
||||
Ok(Box::new(Entity::with_id(entity.0)))
|
||||
Ok(Box::new(Entity::from_id(entity.0)))
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ impl SceneSpawner {
|
||||
.entry(scene_entity.entity)
|
||||
.or_insert_with(|| bevy_ecs::Entity::new())
|
||||
} else {
|
||||
bevy_ecs::Entity::with_id(scene_entity.entity)
|
||||
bevy_ecs::Entity::from_id(scene_entity.entity)
|
||||
};
|
||||
if !world.contains(entity) {
|
||||
world.spawn_as_entity(entity, (1,));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user