This commit is contained in:
Elliott Pierce 2025-05-30 22:53:23 -04:00
parent 3df6ab012b
commit 7b044c0908
4 changed files with 63 additions and 5 deletions

View File

@ -334,7 +334,7 @@ mod tests {
fn entity_mapper() { fn entity_mapper() {
let mut map = EntityHashMap::default(); let mut map = EntityHashMap::default();
let mut world = World::new(); let mut world = World::new();
let mut mapper = SceneEntityMapper::new(&mut map, &mut world); let mut mapper = SceneEntityMapper::new(&mut map, &world);
let mapped_ent = Entity::from_raw_u32(1).unwrap(); let mapped_ent = Entity::from_raw_u32(1).unwrap();
let dead_ref = mapper.get_mapped(mapped_ent); let dead_ref = mapper.get_mapped(mapped_ent);

View File

@ -658,8 +658,9 @@ impl SparseSetIndex for Entity {
} }
} }
/// Allocates [`Entity`] ids uniquely.
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub(crate) struct EntitiesAllocator { pub struct EntitiesAllocator {
free: Vec<Entity>, free: Vec<Entity>,
free_len: AtomicU32, free_len: AtomicU32,
next_row: AtomicU32, next_row: AtomicU32,
@ -761,7 +762,7 @@ impl Entities {
/// Clears all entity information /// Clears all entity information
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.meta.clear() self.meta.clear();
} }
/// Returns the [`EntityLocation`] of an [`Entity`]. /// Returns the [`EntityLocation`] of an [`Entity`].
@ -981,10 +982,13 @@ impl Entities {
/// An error that occurs when a specified [`Entity`] can not be constructed. /// An error that occurs when a specified [`Entity`] can not be constructed.
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)] #[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstructionError { pub enum ConstructionError {
/// The [`Entity`] to construct was invalid.
/// It probably had the wrong generation or was created erroneously.
#[error( #[error(
"The entity's id was invalid: either erroneously created or with the wrong generation." "The entity's id was invalid: either erroneously created or with the wrong generation."
)] )]
InvalidId, InvalidId,
/// The [`Entity`] to construct was already constructed.
#[error("The entity can not be constructed as it already has a location.")] #[error("The entity can not be constructed as it already has a location.")]
AlreadyConstructed, AlreadyConstructed,
} }

View File

@ -2333,6 +2333,9 @@ impl<'w> EntityWorldMut<'w> {
self self
} }
/// Constructs the entity.
/// If the entity has not been constructed or has been destructed, the can construct it.
/// See [`World::construct`] for details.
#[track_caller] #[track_caller]
pub fn construct<B: Bundle>(&mut self, bundle: B) -> Result<&mut Self, ConstructionError> { pub fn construct<B: Bundle>(&mut self, bundle: B) -> Result<&mut Self, ConstructionError> {
let Self { let Self {
@ -2340,11 +2343,26 @@ impl<'w> EntityWorldMut<'w> {
entity, entity,
location, location,
} = self; } = self;
let found = world.construct(*entity, bundle)?; let found = world.construct_with_caller(*entity, bundle, MaybeLocation::caller())?;
*location = found.location; *location = found.location;
Ok(self) Ok(self)
} }
/// A faster version of [`construct`](Self::construct) for the empty bundle.
#[track_caller]
pub fn construct_empty(&mut self) -> Result<&mut Self, ConstructionError> {
let Self {
world,
entity,
location,
} = self;
let found = world.construct_empty_with_caller(*entity, MaybeLocation::caller())?;
*location = found.location;
Ok(self)
}
/// Destructs the entity, without releasing it.
/// This may be later [`constructed`](Self::construct).
#[track_caller] #[track_caller]
pub fn destruct(&mut self) -> &mut Self { pub fn destruct(&mut self) -> &mut Self {
self.destruct_with_caller(MaybeLocation::caller()) self.destruct_with_caller(MaybeLocation::caller())

View File

@ -1053,16 +1053,46 @@ impl World {
} }
/// Spawns an [`Entity`] that is void/null. /// Spawns an [`Entity`] that is void/null.
/// The returned entity id is valid and unique, but it does not correspond to any conceptual entity. /// The returned entity id is valid and unique, but it does not correspond to any conceptual entity yet.
/// The conceptual entity does not exist, and using the id as if it did may produce errors. /// The conceptual entity does not exist, and using the id as if it did may produce errors.
/// It can not be queried, and it has no [`EntityLocation`](crate::entity::EntityLocation). /// It can not be queried, and it has no [`EntityLocation`](crate::entity::EntityLocation).
/// ///
/// This is different from empty entities, which do exist in the world; /// This is different from empty entities, which do exist in the world;
/// they just happen to have no components. /// they just happen to have no components.
///
/// This is particularly useful when spawning entities in special ways.
/// For example, commands uses this to allocate an entity and [`construct`](Self::construct) it later.
/// Note that since this entity is not queryable and its id is not discoverable, loosing the returned [`Entity`] effectively leaks it, never to be used again!
///
/// # Example
///
/// ```
/// # use bevy_ecs::{prelude::*};
/// let mut world = World::new();
/// let entity = world.spawn_null();
/// // wait as long as you like
/// let entity_access = world.construct_empty(entity).unwrap();
/// // treat it as a normal entity
/// entity_access.despawn();
/// ```
pub fn spawn_null(&self) -> Entity { pub fn spawn_null(&self) -> Entity {
self.allocator.alloc() self.allocator.alloc()
} }
/// This releases the entity to be reused by the allocator.
/// It despawns the entity as needed.
///
/// This is useful when the entity may not be constructed.
///
/// # Example
///
/// ```
/// # use bevy_ecs::{prelude::*};
/// let mut world = World::new();
/// let entity = world.spawn_null();
/// // The entity was never constructed.
/// world.release(entity);
/// ```
pub fn release(&mut self, entity: Entity) { pub fn release(&mut self, entity: Entity) {
let Ok(entity) = self.get_entity_mut(entity) else { let Ok(entity) = self.get_entity_mut(entity) else {
return; // Already released then. return; // Already released then.
@ -1085,6 +1115,11 @@ impl World {
.free(self.entities.mark_free(entity, generations)); .free(self.entities.mark_free(entity, generations));
} }
/// Constructs the bundle on the entity.
/// If the entity can not be constructed for any reason, returns an error.
///
/// If it succeeds, this declares the entity to have this bundle.
/// It is possible to construct an `entity` that has not been allocated yet.
#[track_caller] #[track_caller]
pub fn construct<B: Bundle>( pub fn construct<B: Bundle>(
&mut self, &mut self,
@ -1137,6 +1172,7 @@ impl World {
entity entity
} }
/// A faster version of [`construct`](Self::construct) for the empty bundle.
#[track_caller] #[track_caller]
pub fn construct_empty( pub fn construct_empty(
&mut self, &mut self,