mirror of
https://github.com/lisk77/comet.git
synced 2025-12-12 09:08:49 +00:00
refactor(ecs)!: reworked the entity id system
This commit is contained in:
parent
eced6ddf3f
commit
607bf94f1e
7 changed files with 198 additions and 95 deletions
|
|
@ -1,5 +1,7 @@
|
|||
use comet_colors::{Color as ColorTrait, LinearRgba};
|
||||
use comet_ecs::{Camera2D, Component, Entity, Render2D, Scene, Text, Transform2D, Transform3D};
|
||||
use comet_ecs::{
|
||||
Camera2D, Component, Entity, EntityId, Render2D, Scene, Text, Transform2D, Transform3D,
|
||||
};
|
||||
use comet_input::keyboard::Key;
|
||||
use comet_log::*;
|
||||
use comet_renderer::renderer::Renderer;
|
||||
|
|
@ -163,22 +165,22 @@ impl App {
|
|||
}
|
||||
|
||||
/// Creates a new entity and returns its ID.
|
||||
pub fn new_entity(&mut self) -> usize {
|
||||
self.scene.new_entity() as usize
|
||||
pub fn new_entity(&mut self) -> EntityId {
|
||||
self.scene.new_entity()
|
||||
}
|
||||
|
||||
/// Deletes an entity by its ID.
|
||||
pub fn delete_entity(&mut self, entity_id: usize) {
|
||||
pub fn delete_entity(&mut self, entity_id: EntityId) {
|
||||
self.scene.delete_entity(entity_id)
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to an entity by its ID.
|
||||
pub fn get_entity(&self, entity_id: usize) -> Option<&Entity> {
|
||||
pub fn get_entity(&self, entity_id: EntityId) -> Option<&Entity> {
|
||||
self.scene.get_entity(entity_id)
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to an entity by its ID.
|
||||
pub fn get_entity_mut(&mut self, entity_id: usize) -> Option<&mut Entity> {
|
||||
pub fn get_entity_mut(&mut self, entity_id: EntityId) -> Option<&mut Entity> {
|
||||
self.scene.get_entity_mut(entity_id)
|
||||
}
|
||||
|
||||
|
|
@ -194,29 +196,29 @@ impl App {
|
|||
|
||||
/// Adds a component to an entity by its ID and an instance of the component.
|
||||
/// Overwrites the previous component if another component of the same type is added.
|
||||
pub fn add_component<C: Component>(&mut self, entity_id: usize, component: C) {
|
||||
pub fn add_component<C: Component>(&mut self, entity_id: EntityId, component: C) {
|
||||
self.scene.add_component(entity_id, component)
|
||||
}
|
||||
|
||||
/// Removes a component from an entity by its ID.
|
||||
pub fn remove_component<C: Component>(&mut self, entity_id: usize) {
|
||||
pub fn remove_component<C: Component>(&mut self, entity_id: EntityId) {
|
||||
self.scene.remove_component::<C>(entity_id)
|
||||
}
|
||||
|
||||
/// Returns a reference to a component of an entity by its ID.
|
||||
pub fn get_component<C: Component>(&self, entity_id: usize) -> Option<&C> {
|
||||
pub fn get_component<C: Component>(&self, entity_id: EntityId) -> Option<&C> {
|
||||
self.scene.get_component::<C>(entity_id)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to a component of an entity by its ID.
|
||||
pub fn get_component_mut<C: Component>(&mut self, entity_id: usize) -> Option<&mut C> {
|
||||
pub fn get_component_mut<C: Component>(&mut self, entity_id: EntityId) -> Option<&mut C> {
|
||||
self.scene.get_component_mut::<C>(entity_id)
|
||||
}
|
||||
|
||||
/// Returns a list of entities that have the given components.
|
||||
/// The amount of queriable components is limited to 3 such that the `Archetype` creation is more efficient.
|
||||
/// Otherwise it would be a factorial complexity chaos.
|
||||
pub fn get_entities_with(&self, components: Vec<TypeId>) -> Vec<usize> {
|
||||
pub fn get_entities_with(&self, components: Vec<TypeId>) -> Vec<EntityId> {
|
||||
self.scene.get_entities_with(components)
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +235,7 @@ impl App {
|
|||
}
|
||||
|
||||
/// Returns whether an entity has the given component.
|
||||
pub fn has<C: Component>(&self, entity_id: usize) -> bool {
|
||||
pub fn has<C: Component>(&self, entity_id: EntityId) -> bool {
|
||||
self.scene.has::<C>(entity_id)
|
||||
}
|
||||
|
||||
|
|
@ -243,7 +245,7 @@ impl App {
|
|||
}
|
||||
|
||||
/// Spawns a prefab with the given name.
|
||||
pub fn spawn_prefab(&mut self, name: &str) -> Option<usize> {
|
||||
pub fn spawn_prefab(&mut self, name: &str) -> Option<EntityId> {
|
||||
self.scene.spawn_prefab(name)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -436,13 +436,15 @@ impl Camera for Camera2D {
|
|||
let entities = scene.entities();
|
||||
let mut visible_entities = Vec::new();
|
||||
for entity in entities {
|
||||
let id = *entity.clone().unwrap().id() as usize;
|
||||
if let Some(ent) = entity.clone() {
|
||||
let id = ent.id();
|
||||
if let Some(transform) = scene.get_component::<Transform2D>(id) {
|
||||
if self.in_view_frustum(camera_position, transform.position()) {
|
||||
visible_entities.push(entity.clone().unwrap());
|
||||
visible_entities.push(ent);
|
||||
}
|
||||
} else {
|
||||
error!("Entity {} missing Transform2D", id);
|
||||
error!("Entity {} missing Transform2D", id.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
visible_entities
|
||||
|
|
|
|||
|
|
@ -1,21 +1,35 @@
|
|||
use bit_set::BitSet;
|
||||
|
||||
/// Handle used to reference entities safely. Contains an index into the entity
|
||||
/// storage and a generation counter to detect stale handles.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct EntityId {
|
||||
pub index: u32,
|
||||
pub gen: u32,
|
||||
}
|
||||
|
||||
impl Default for EntityId {
|
||||
fn default() -> Self {
|
||||
Self { index: 0, gen: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Entity {
|
||||
id: u32,
|
||||
components: BitSet
|
||||
id: EntityId,
|
||||
components: BitSet,
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
pub(crate) fn new(id: u32) -> Self {
|
||||
pub(crate) fn new(index: u32, gen: u32) -> Self {
|
||||
Self {
|
||||
id,
|
||||
components: BitSet::new()
|
||||
id: EntityId { index, gen },
|
||||
components: BitSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &u32 {
|
||||
&self.id
|
||||
pub fn id(&self) -> EntityId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub(crate) fn add_component(&mut self, component_index: usize) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use comet_structs::FlatMap;
|
||||
|
||||
pub type PrefabFactory = fn(&mut crate::Scene) -> usize;
|
||||
pub type PrefabFactory = fn(&mut crate::Scene) -> crate::EntityId;
|
||||
|
||||
pub(crate) struct PrefabManager {
|
||||
pub(crate) prefabs: FlatMap<String, PrefabFactory>,
|
||||
|
|
@ -26,8 +26,8 @@ impl PrefabManager {
|
|||
macro_rules! register_prefab {
|
||||
($scene:expr, $name:expr, $($component:expr),* $(,)?) => {
|
||||
{
|
||||
fn prefab_factory(scene: &mut $crate::Scene) -> usize {
|
||||
let entity = scene.new_entity() as usize;
|
||||
fn prefab_factory(scene: &mut $crate::Scene) -> $crate::EntityId {
|
||||
let entity = scene.new_entity();
|
||||
$(
|
||||
scene.add_component(entity, $component);
|
||||
)*
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::archetypes::Archetypes;
|
||||
use crate::prefabs::PrefabManager;
|
||||
use crate::{Component, Entity, IdQueue};
|
||||
use crate::{Component, Entity, EntityId, IdQueue};
|
||||
use comet_log::*;
|
||||
use comet_structs::*;
|
||||
use std::any::TypeId;
|
||||
|
|
@ -8,6 +8,7 @@ use std::any::TypeId;
|
|||
pub struct Scene {
|
||||
id_queue: IdQueue,
|
||||
next_id: u32,
|
||||
generations: Vec<u32>,
|
||||
entities: Vec<Option<Entity>>,
|
||||
components: ComponentStorage,
|
||||
archetypes: Archetypes,
|
||||
|
|
@ -19,6 +20,7 @@ impl Scene {
|
|||
Self {
|
||||
id_queue: IdQueue::new(),
|
||||
next_id: 0,
|
||||
generations: Vec::new(),
|
||||
entities: Vec::new(),
|
||||
components: ComponentStorage::new(),
|
||||
archetypes: Archetypes::new(),
|
||||
|
|
@ -43,45 +45,80 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_alive(&self, id: EntityId) -> bool {
|
||||
self.generations
|
||||
.get(id.index as usize)
|
||||
.is_some_and(|g| *g == id.gen)
|
||||
&& self
|
||||
.entities
|
||||
.get(id.index as usize)
|
||||
.is_some_and(|e| e.is_some())
|
||||
}
|
||||
|
||||
/// Retuns the `Vec` of `Option<Entity>` which contains all the entities in the current Scene.
|
||||
pub fn entities(&self) -> &Vec<Option<Entity>> {
|
||||
&self.entities
|
||||
}
|
||||
|
||||
/// Creates a new entity and returns its ID.
|
||||
pub fn new_entity(&mut self) -> u32 {
|
||||
let id = self.next_id;
|
||||
if (self.next_id as usize) >= self.entities.len() {
|
||||
self.entities.push(Some(Entity::new(self.next_id)));
|
||||
self.get_next_id();
|
||||
info!("Created entity! ID: {}", id);
|
||||
return id;
|
||||
pub fn new_entity(&mut self) -> EntityId {
|
||||
let index = self.next_id;
|
||||
let gen = if (index as usize) >= self.generations.len() {
|
||||
self.generations.push(0);
|
||||
0
|
||||
} else {
|
||||
self.generations[index as usize]
|
||||
};
|
||||
|
||||
if (index as usize) >= self.entities.len() {
|
||||
self.entities.push(Some(Entity::new(index, gen)));
|
||||
} else {
|
||||
self.entities[index as usize] = Some(Entity::new(index, gen));
|
||||
}
|
||||
self.entities[self.next_id as usize] = Some(Entity::new(self.next_id));
|
||||
|
||||
let id = EntityId { index, gen };
|
||||
self.get_next_id();
|
||||
info!("Created entity! ID: {}", id);
|
||||
info!("Created entity! ID: {} (gen {})", id.index, id.gen);
|
||||
id
|
||||
}
|
||||
|
||||
/// Gets an immutable reference to an entity by its ID.
|
||||
pub fn get_entity(&self, entity_id: usize) -> Option<&Entity> {
|
||||
self.entities.get(entity_id).and_then(|e| e.as_ref())
|
||||
pub fn get_entity(&self, entity_id: EntityId) -> Option<&Entity> {
|
||||
if !self.is_alive(entity_id) {
|
||||
return None;
|
||||
}
|
||||
self.entities
|
||||
.get(entity_id.index as usize)
|
||||
.and_then(|e| e.as_ref())
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to an entity by its ID.
|
||||
pub fn get_entity_mut(&mut self, entity_id: usize) -> Option<&mut Entity> {
|
||||
self.entities.get_mut(entity_id).and_then(|e| e.as_mut())
|
||||
pub fn get_entity_mut(&mut self, entity_id: EntityId) -> Option<&mut Entity> {
|
||||
if !self.is_alive(entity_id) {
|
||||
return None;
|
||||
}
|
||||
self.entities
|
||||
.get_mut(entity_id.index as usize)
|
||||
.and_then(|e| e.as_mut())
|
||||
}
|
||||
|
||||
/// Deletes an entity by its ID.
|
||||
pub fn delete_entity(&mut self, entity_id: usize) {
|
||||
self.remove_entity_from_archetype(entity_id as u32, self.get_component_set(entity_id));
|
||||
self.entities[entity_id] = None;
|
||||
info!("Deleted entity! ID: {}", entity_id);
|
||||
for (_, value) in self.components.iter_mut() {
|
||||
value.remove::<u8>(entity_id);
|
||||
pub fn delete_entity(&mut self, entity_id: EntityId) {
|
||||
if !self.is_alive(entity_id) {
|
||||
return;
|
||||
}
|
||||
self.id_queue.sorted_enqueue(entity_id as u32);
|
||||
|
||||
let idx = entity_id.index as usize;
|
||||
self.remove_entity_from_archetype(entity_id.index, self.get_component_set(idx));
|
||||
self.entities[idx] = None;
|
||||
info!("Deleted entity! ID: {}", entity_id.index);
|
||||
for (_, value) in self.components.iter_mut() {
|
||||
value.remove::<u8>(idx);
|
||||
}
|
||||
if let Some(gen) = self.generations.get_mut(idx) {
|
||||
*gen = gen.wrapping_add(1);
|
||||
}
|
||||
self.id_queue.sorted_enqueue(entity_id.index);
|
||||
self.get_next_id();
|
||||
}
|
||||
|
||||
|
|
@ -159,13 +196,23 @@ impl Scene {
|
|||
|
||||
/// Adds a component to an entity by its ID and an instance of the component.
|
||||
/// Overwrites the previous component if another component of the same type is added.
|
||||
pub fn add_component<C: Component + 'static>(&mut self, entity_id: usize, component: C) {
|
||||
let old_component_set = self.get_component_set(entity_id);
|
||||
if !old_component_set.to_vec().is_empty() {
|
||||
self.remove_entity_from_archetype(entity_id as u32, old_component_set);
|
||||
pub fn add_component<C: Component + 'static>(&mut self, entity_id: EntityId, component: C) {
|
||||
if !self.is_alive(entity_id) {
|
||||
error!(
|
||||
"Attempted to add component {} to dead entity {}",
|
||||
C::type_name(),
|
||||
entity_id.index
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
self.components.set_component(entity_id, component);
|
||||
let old_component_set = self.get_component_set(entity_id.index as usize);
|
||||
if !old_component_set.to_vec().is_empty() {
|
||||
self.remove_entity_from_archetype(entity_id.index, old_component_set);
|
||||
}
|
||||
|
||||
self.components
|
||||
.set_component(entity_id.index as usize, component);
|
||||
if let Some(component_index) = self
|
||||
.components
|
||||
.keys()
|
||||
|
|
@ -175,36 +222,43 @@ impl Scene {
|
|||
if let Some(entity) = self.get_entity_mut(entity_id) {
|
||||
entity.add_component(component_index);
|
||||
} else {
|
||||
error!("Attempted to add component to non-existent entity {}", entity_id);
|
||||
error!(
|
||||
"Attempted to add component to non-existent entity {}",
|
||||
entity_id.index
|
||||
);
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"Component {} not registered, cannot add to entity {}",
|
||||
C::type_name(),
|
||||
entity_id
|
||||
entity_id.index
|
||||
);
|
||||
}
|
||||
|
||||
let new_component_set = self.get_component_set(entity_id);
|
||||
let new_component_set = self.get_component_set(entity_id.index as usize);
|
||||
|
||||
if !self.archetypes.contains_archetype(&new_component_set) {
|
||||
self.create_archetype(new_component_set.clone());
|
||||
}
|
||||
|
||||
self.add_entity_to_archetype(entity_id as u32, new_component_set);
|
||||
self.add_entity_to_archetype(entity_id.index, new_component_set);
|
||||
|
||||
info!(
|
||||
"Added component {} to entity {}!",
|
||||
C::type_name(),
|
||||
entity_id
|
||||
entity_id.index
|
||||
);
|
||||
}
|
||||
|
||||
pub fn remove_component<C: Component + 'static>(&mut self, entity_id: usize) {
|
||||
let old_component_set = self.get_component_set(entity_id);
|
||||
self.remove_entity_from_archetype(entity_id as u32, old_component_set);
|
||||
pub fn remove_component<C: Component + 'static>(&mut self, entity_id: EntityId) {
|
||||
if !self.is_alive(entity_id) {
|
||||
return;
|
||||
}
|
||||
let old_component_set = self.get_component_set(entity_id.index as usize);
|
||||
self.remove_entity_from_archetype(entity_id.index, old_component_set);
|
||||
|
||||
self.components.remove_component::<C>(entity_id);
|
||||
self.components
|
||||
.remove_component::<C>(entity_id.index as usize);
|
||||
if let Some(component_index) = self
|
||||
.components
|
||||
.keys()
|
||||
|
|
@ -216,48 +270,73 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
|
||||
let new_component_set = self.get_component_set(entity_id);
|
||||
let new_component_set = self.get_component_set(entity_id.index as usize);
|
||||
|
||||
if !new_component_set.to_vec().is_empty() {
|
||||
if !self.archetypes.contains_archetype(&new_component_set) {
|
||||
self.create_archetype(new_component_set.clone());
|
||||
}
|
||||
|
||||
self.add_entity_to_archetype(entity_id as u32, new_component_set);
|
||||
self.add_entity_to_archetype(entity_id.index, new_component_set);
|
||||
}
|
||||
|
||||
info!(
|
||||
"Removed component {} from entity {}!",
|
||||
C::type_name(),
|
||||
entity_id
|
||||
entity_id.index
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns a reference to a component of an entity by its ID.
|
||||
pub fn get_component<C: Component + 'static>(&self, entity_id: usize) -> Option<&C> {
|
||||
self.components.get_component::<C>(entity_id)
|
||||
pub fn get_component<C: Component + 'static>(&self, entity_id: EntityId) -> Option<&C> {
|
||||
if !self.is_alive(entity_id) {
|
||||
return None;
|
||||
}
|
||||
self.components
|
||||
.get_component::<C>(entity_id.index as usize)
|
||||
}
|
||||
|
||||
pub fn get_component_mut<C: Component + 'static>(
|
||||
&mut self,
|
||||
entity_id: usize,
|
||||
entity_id: EntityId,
|
||||
) -> Option<&mut C> {
|
||||
self.components.get_component_mut::<C>(entity_id)
|
||||
if !self.is_alive(entity_id) {
|
||||
return None;
|
||||
}
|
||||
self.components
|
||||
.get_component_mut::<C>(entity_id.index as usize)
|
||||
}
|
||||
|
||||
pub fn has<C: Component + 'static>(&self, entity_id: usize) -> bool {
|
||||
self.components.get_component::<C>(entity_id).is_some()
|
||||
pub fn has<C: Component + 'static>(&self, entity_id: EntityId) -> bool {
|
||||
self.is_alive(entity_id)
|
||||
&& self
|
||||
.components
|
||||
.get_component::<C>(entity_id.index as usize)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
/// Returns a list of entities that have the given components.
|
||||
pub fn get_entities_with(&self, components: Vec<TypeId>) -> Vec<usize> {
|
||||
pub fn get_entities_with(&self, components: Vec<TypeId>) -> Vec<EntityId> {
|
||||
let component_set = ComponentSet::from_ids(components);
|
||||
let mut result = Vec::new();
|
||||
|
||||
for archetype_set in self.archetypes.component_sets() {
|
||||
if component_set.is_subset(&archetype_set) {
|
||||
if let Some(entities) = self.archetypes.get_archetype(&archetype_set) {
|
||||
result.extend(entities.iter().map(|x| *x as usize));
|
||||
for index in entities.iter() {
|
||||
if let Some(gen) = self.generations.get(*index as usize) {
|
||||
if self
|
||||
.entities
|
||||
.get(*index as usize)
|
||||
.is_some_and(|e| e.is_some())
|
||||
{
|
||||
result.push(EntityId {
|
||||
index: *index,
|
||||
gen: *gen,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -286,8 +365,9 @@ impl Scene {
|
|||
.components
|
||||
.get_two_mut(&C::type_id(), &K::type_id());
|
||||
|
||||
let c_opt = c_set.and_then(|set| set.get_mut::<C>(entity));
|
||||
let k_opt = k_set.and_then(|set| set.get_mut::<K>(entity));
|
||||
let idx = entity.index as usize;
|
||||
let c_opt = c_set.and_then(|set| set.get_mut::<C>(idx));
|
||||
let k_opt = k_set.and_then(|set| set.get_mut::<K>(idx));
|
||||
|
||||
if let (Some(c), Some(k)) = (c_opt, k_opt) {
|
||||
func(c, k);
|
||||
|
|
@ -301,7 +381,7 @@ impl Scene {
|
|||
}
|
||||
|
||||
/// Spawns a prefab with the given name.
|
||||
pub fn spawn_prefab(&mut self, name: &str) -> Option<usize> {
|
||||
pub fn spawn_prefab(&mut self, name: &str) -> Option<EntityId> {
|
||||
if self.prefabs.has_prefab(name) {
|
||||
if let Some(factory) = self.prefabs.prefabs.get(&name.to_string()) {
|
||||
let factory = *factory; // Copy the function pointer
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ impl CameraManager {
|
|||
self.cameras.get(self.active_camera).unwrap()
|
||||
}
|
||||
|
||||
pub fn update_from_scene(&mut self, scene: &comet_ecs::Scene, camera_entities: Vec<usize>) {
|
||||
pub fn update_from_scene(
|
||||
&mut self,
|
||||
scene: &comet_ecs::Scene,
|
||||
camera_entities: Vec<comet_ecs::EntityId>,
|
||||
) {
|
||||
self.cameras.clear();
|
||||
|
||||
let mut cameras_with_priority: Vec<(RenderCamera, u8)> = Vec::new();
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ pub struct Renderer2D<'a> {
|
|||
resource_manager: GraphicResourceManager,
|
||||
camera_manager: CameraManager,
|
||||
render_passes: Vec<RenderPass>,
|
||||
cached_render_entities: Vec<usize>,
|
||||
cached_render_entities: Vec<comet_ecs::EntityId>,
|
||||
last_frame_time: std::time::Instant,
|
||||
delta_time: f32,
|
||||
}
|
||||
|
|
@ -754,7 +754,8 @@ impl<'a> Renderer2D<'a> {
|
|||
let mut dirty_sort = self.cached_render_entities.len() != unsorted_entities.len();
|
||||
|
||||
if !dirty_sort {
|
||||
let unsorted_set: HashSet<usize> = unsorted_entities.iter().copied().collect();
|
||||
let unsorted_set: HashSet<comet_ecs::EntityId> =
|
||||
unsorted_entities.iter().copied().collect();
|
||||
dirty_sort = self
|
||||
.cached_render_entities
|
||||
.iter()
|
||||
|
|
@ -952,7 +953,7 @@ impl<'a> Renderer2D<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn setup_camera(&mut self, scene: &comet_ecs::Scene, cameras: Vec<usize>) {
|
||||
fn setup_camera(&mut self, scene: &comet_ecs::Scene, cameras: Vec<comet_ecs::EntityId>) {
|
||||
if cameras.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue