From f010b39c3ca46d5cb6b00e02b48f237d78a25acb Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Wed, 19 Jun 2019 16:13:02 -0700 Subject: [PATCH] entity tracker system --- encompass-cs/ComponentManager.cs | 70 +++++++++----- encompass-cs/Entity.cs | 2 +- encompass-cs/EntityManager.cs | 115 +++++++++++++++++++++-- encompass-cs/IEntityTracker.cs | 13 +++ encompass-cs/World.cs | 4 + encompass-cs/WorldBuilder.cs | 20 +++- encompass-cs/renderers/EntityRenderer.cs | 21 ++++- 7 files changed, 205 insertions(+), 40 deletions(-) create mode 100644 encompass-cs/IEntityTracker.cs diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 38496aa..78ef122 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -11,19 +11,41 @@ namespace Encompass private Dictionary> entityIDToComponentIDs = new Dictionary>(); private Dictionary componentIDToEntityID = new Dictionary(); - private Dictionary> activeComponents = new Dictionary>(); - private Dictionary> inactiveComponents = new Dictionary>(); + private Dictionary> typeToComponentIDs = new Dictionary>(); + + private List activeComponents = new List(); + private List inactiveComponents = new List(); private List componentsToActivate = new List(); private List componentsToDeactivate = new List(); private List componentsToRemove = new List(); + //shared references with EntityManager + private List entitiesWithAddedComponents; + private List entitiesWithRemovedComponents; + + public ComponentManager( + List entitiesWithAddedComponents, + List entitiesWithRemovedComponents + ) + { + this.entitiesWithAddedComponents = entitiesWithAddedComponents; + this.entitiesWithRemovedComponents = entitiesWithRemovedComponents; + } + internal Guid AddComponent(Guid entityID, TComponent component) where TComponent : struct, IComponent { var componentID = Guid.NewGuid(); IDToComponent[componentID] = component; componentIDToType[componentID] = typeof(TComponent); + + if (!typeToComponentIDs.ContainsKey(typeof(TComponent))) + { + typeToComponentIDs.Add(typeof(TComponent), new List()); + } + + typeToComponentIDs[typeof(TComponent)].Add(componentID); if (!entityIDToComponentIDs.ContainsKey(entityID)) { @@ -33,33 +55,30 @@ namespace Encompass entityIDToComponentIDs[entityID].Add(componentID); componentIDToEntityID[componentID] = entityID; - if (!activeComponents.ContainsKey(typeof(TComponent))) - { - activeComponents.Add(typeof(TComponent), new List()); - inactiveComponents.Add(typeof(TComponent), new List()); - } - + inactiveComponents.Add(componentID); MarkForActivation(componentID); + entitiesWithAddedComponents.Add(entityID); + return componentID; } internal IEnumerable> GetComponentsByEntity(Guid entityID) { - return entityIDToComponentIDs[entityID].Select((id) => new KeyValuePair(id, IDToComponent[id])); + return entityIDToComponentIDs[entityID].Intersect(activeComponents).Select((id) => new KeyValuePair(id, IDToComponent[id])); } internal IEnumerable> GetActiveComponentsByType() where TComponent : struct, IComponent { - return activeComponents.ContainsKey(typeof(TComponent)) ? - activeComponents[typeof(TComponent)].Select((id) => new KeyValuePair(id, (TComponent)IDToComponent[id])) : + return typeToComponentIDs.ContainsKey(typeof(TComponent)) ? + typeToComponentIDs[typeof(TComponent)].Select((id) => new KeyValuePair(id, (TComponent)IDToComponent[id])) : Enumerable.Empty>(); } internal IEnumerable> GetActiveComponentsByType(Type type) { - return activeComponents.ContainsKey(type) ? - activeComponents[type].Select((id) => new KeyValuePair(id, IDToComponent[id])) : + return typeToComponentIDs.ContainsKey(type) ? + typeToComponentIDs[type].Select((id) => new KeyValuePair(id, IDToComponent[id])) : Enumerable.Empty>(); } @@ -82,6 +101,11 @@ namespace Encompass return entityComponents.Intersect(activeComponentsByType); } + internal IEnumerable GetAllComponentTypesOfEntity(Guid entityID) + { + return entityIDToComponentIDs[entityID].Select((id) => componentIDToType[id]); + } + internal bool EntityHasComponentOfType(Guid entityID) where TComponent : struct, IComponent { return GetComponentsByEntityAndType(entityID).Any(); @@ -119,9 +143,7 @@ namespace Encompass foreach (var componentID in componentIDs) { - var component = IDToComponent[componentID]; - activeComponents[component.GetType()].Remove(componentID); - inactiveComponents[component.GetType()].Remove(componentID); + MarkForRemoval(componentID); } entityIDToComponentIDs.Remove(entityID); @@ -147,8 +169,10 @@ namespace Encompass foreach (var componentID in componentsToActivate) { var component = IDToComponent[componentID]; - activeComponents[component.GetType()].Add(componentID); - inactiveComponents[component.GetType()].Remove(componentID); + if (inactiveComponents.Remove(componentID)) + { + activeComponents.Add(componentID); + } } componentsToActivate.Clear(); @@ -159,8 +183,10 @@ namespace Encompass foreach (var componentID in componentsToDeactivate) { var component = IDToComponent[componentID]; - activeComponents[component.GetType()].Remove(componentID); - inactiveComponents[component.GetType()].Add(componentID); + if (activeComponents.Remove(componentID)) + { + inactiveComponents.Add(componentID); + } } componentsToDeactivate.Clear(); @@ -171,8 +197,8 @@ namespace Encompass foreach (var componentID in componentsToRemove) { var component = IDToComponent[componentID]; - activeComponents[component.GetType()].Remove(componentID); - inactiveComponents[component.GetType()].Remove(componentID); + activeComponents.Remove(componentID); + inactiveComponents.Remove(componentID); } componentsToRemove.Clear(); diff --git a/encompass-cs/Entity.cs b/encompass-cs/Entity.cs index 966012f..20180c1 100644 --- a/encompass-cs/Entity.cs +++ b/encompass-cs/Entity.cs @@ -18,7 +18,7 @@ namespace Encompass public Guid AddComponent(TComponent component) where TComponent : struct, IComponent { - return componentManager.AddComponent(id, component); + return componentManager.AddComponent(id, component); } public IEnumerable> GetComponents() where TComponent : struct, IComponent diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index 7bf3d30..26ced7e 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -5,23 +5,35 @@ namespace Encompass { internal class EntityManager { - private List entities = new List(); private Dictionary IDToEntity = new Dictionary(); - private List entitiesMarkedForDestroy = new List(); + private List entitiesMarkedForDestroy = new List(); + + private Dictionary> componentTypeToEntityTrackers = new Dictionary>(); + private Dictionary> entityToEntityTrackers = new Dictionary>(); + + private List entitiesWithAddedComponents; + private List entitiesWithRemovedComponents; private ComponentManager componentManager; public EntityManager( - ComponentManager componentManager + ComponentManager componentManager, + List entitiesWithAddedComponents, + List entitiesWithRemovedComponents ) { this.componentManager = componentManager; + this.entitiesWithAddedComponents = entitiesWithAddedComponents; + this.entitiesWithRemovedComponents = entitiesWithRemovedComponents; } public Entity CreateEntity() { - return new Entity(NextID(), componentManager); + var id = NextID(); + var entity = new Entity(id, componentManager); + IDToEntity[id] = entity; + return entity; } public Entity GetEntity(Guid id) @@ -29,22 +41,109 @@ namespace Encompass return IDToEntity[id]; } - public void MarkForDestroy(Entity entity) + public void MarkForDestroy(Guid entityID) { - entitiesMarkedForDestroy.Add(entity); + entitiesMarkedForDestroy.Add(entityID); } - internal void DestroyMarkedEntities() + public void DestroyMarkedEntities() { - foreach (var entity in entitiesMarkedForDestroy) + foreach (var entityID in entitiesMarkedForDestroy) { + var entity = IDToEntity[entityID]; entity.RemoveAllComponents(); + IDToEntity.Remove(entityID); + entityToEntityTrackers.Remove(entityID); } + + entitiesMarkedForDestroy.Clear(); } private Guid NextID() { return Guid.NewGuid(); } + + public void RegisterEntityTracker(IEntityTracker entityTracker) + { + foreach (var componentType in entityTracker.ComponentTypes) + { + if (!componentTypeToEntityTrackers.ContainsKey(componentType)) + { + componentTypeToEntityTrackers.Add(componentType, new HashSet()); + } + + componentTypeToEntityTrackers[componentType].Add(entityTracker); + } + + if (entityTracker is EntityRenderer) + { + var entityRenderer = entityTracker as EntityRenderer; + if (!componentTypeToEntityTrackers.ContainsKey(entityRenderer.DrawComponentType)) + { + componentTypeToEntityTrackers.Add(entityRenderer.DrawComponentType, new HashSet()); + } + + componentTypeToEntityTrackers[entityRenderer.DrawComponentType].Add(entityRenderer); + } + } + + public void RegisterDirtyEntityWithAddedComponents(Guid entityID) + { + entitiesWithAddedComponents.Add(entityID); + } + + public void RegisterDirtyEntityWithRemovedComponents(Guid entityID) + { + entitiesWithRemovedComponents.Add(entityID); + } + + public void CheckEntitiesWithAddedComponents() + { + foreach (var entityID in entitiesWithAddedComponents) + { + CheckAndRegisterEntity(entityID); + } + + entitiesWithAddedComponents.Clear(); + } + + public void CheckEntitiesWithRemovedComponents() + { + foreach (var entityID in entitiesWithRemovedComponents) + { + if (entityToEntityTrackers.ContainsKey(entityID)) + { + foreach (var engine in entityToEntityTrackers[entityID]) + { + engine.CheckAndUntrackEntity(entityID); + } + } + } + + entitiesWithRemovedComponents.Clear(); + } + + private void CheckAndRegisterEntity(Guid entityID) + { + var componentTypes = componentManager.GetAllComponentTypesOfEntity(entityID); + foreach (var componentType in componentTypes) + { + if (componentTypeToEntityTrackers.ContainsKey(componentType)) + { + foreach (var entityTracker in componentTypeToEntityTrackers[componentType]) + { + if (entityTracker.CheckAndTrackEntity(entityID)) + { + if (!entityToEntityTrackers.ContainsKey(entityID)) + { + entityToEntityTrackers.Add(entityID, new HashSet()); + } + entityToEntityTrackers[entityID].Add(entityTracker); + } + } + } + } + } } } diff --git a/encompass-cs/IEntityTracker.cs b/encompass-cs/IEntityTracker.cs new file mode 100644 index 0000000..050090c --- /dev/null +++ b/encompass-cs/IEntityTracker.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Encompass +{ + public interface IEntityTracker + { + IEnumerable ComponentTypes { get; } + + bool CheckAndTrackEntity(Guid entityID); + bool CheckAndUntrackEntity(Guid entityID); + } +} diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index 6ca62cf..bd53b27 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -34,9 +34,13 @@ namespace Encompass messageManager.ClearMessages(); entityManager.DestroyMarkedEntities(); + componentManager.ActivateComponents(); componentManager.DeactivateComponents(); componentManager.RemoveComponents(); + + entityManager.CheckEntitiesWithAddedComponents(); + entityManager.CheckEntitiesWithRemovedComponents(); } public void Draw() diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 6a12b2a..e47c2fa 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -20,8 +20,10 @@ namespace Encompass public WorldBuilder() { - componentManager = new ComponentManager(); - entityManager = new EntityManager(componentManager); + var entitiesWithAddedComponents = new List(); + var entitiesWithRemovedComponents = new List(); + componentManager = new ComponentManager(entitiesWithAddedComponents, entitiesWithRemovedComponents); + entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents); messageManager = new MessageManager(); renderManager = new RenderManager(entityManager, componentManager); } @@ -43,6 +45,11 @@ namespace Encompass engineGraph.AddVertex(engine); + if (engine is IEntityTracker) + { + entityManager.RegisterEntityTracker(engine as IEntityTracker); + } + var emitMessageAttribute = engine.GetType().GetCustomAttribute(false); if (emitMessageAttribute != null) { @@ -98,6 +105,7 @@ namespace Encompass if (renderer is EntityRenderer) { + entityManager.RegisterEntityTracker(renderer as IEntityTracker); renderManager.RegisterEntityRenderer(renderer as EntityRenderer); } else if (renderer is GeneralRenderer) @@ -182,8 +190,12 @@ namespace Encompass renderManager ); - this.componentManager.ActivateComponents(); - this.componentManager.RemoveComponents(); + componentManager.ActivateComponents(); + componentManager.DeactivateComponents(); + componentManager.RemoveComponents(); + + entityManager.CheckEntitiesWithAddedComponents(); + entityManager.CheckEntitiesWithRemovedComponents(); return world; } diff --git a/encompass-cs/renderers/EntityRenderer.cs b/encompass-cs/renderers/EntityRenderer.cs index 18d148e..9e25be7 100644 --- a/encompass-cs/renderers/EntityRenderer.cs +++ b/encompass-cs/renderers/EntityRenderer.cs @@ -1,18 +1,29 @@ using System; +using System.Reflection; using System.Collections.Generic; -using System.Text; namespace Encompass { - public abstract class EntityRenderer : Renderer + public abstract class EntityRenderer : Renderer, IEntityTracker { private readonly List componentTypes = new List(); - private readonly Type drawComponentType; - private EntityTracker entityTracker = new EntityTracker(); + public IEnumerable ComponentTypes { get { return componentTypes; } } + public Type DrawComponentType { get; } + public abstract void Render(Entity entity); + public EntityRenderer() + { + var rendersAttribute = GetType().GetCustomAttribute(false); + if (rendersAttribute != null) + { + componentTypes = rendersAttribute.componentTypes; + DrawComponentType = rendersAttribute.drawComponentType; + } + } + public bool CheckAndTrackEntity(Guid entityID) { var entity = GetEntity(entityID); @@ -37,7 +48,7 @@ namespace Encompass internal bool CheckEntity(Entity entity) { return EntityChecker.CheckEntity(entity, componentTypes) && - entity.HasComponent(drawComponentType); + entity.HasComponent(DrawComponentType); } } }