entity tracker system

pull/5/head
Evan Hemsley 2019-06-19 16:13:02 -07:00
parent 5941029927
commit f010b39c3c
7 changed files with 205 additions and 40 deletions

View File

@ -11,13 +11,28 @@ namespace Encompass
private Dictionary<Guid, List<Guid>> entityIDToComponentIDs = new Dictionary<Guid, List<Guid>>(); private Dictionary<Guid, List<Guid>> entityIDToComponentIDs = new Dictionary<Guid, List<Guid>>();
private Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>(); private Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
private Dictionary<Type, List<Guid>> activeComponents = new Dictionary<Type, List<Guid>>(); private Dictionary<Type, List<Guid>> typeToComponentIDs = new Dictionary<Type, List<Guid>>();
private Dictionary<Type, List<Guid>> inactiveComponents = new Dictionary<Type, List<Guid>>();
private List<Guid> activeComponents = new List<Guid>();
private List<Guid> inactiveComponents = new List<Guid>();
private List<Guid> componentsToActivate = new List<Guid>(); private List<Guid> componentsToActivate = new List<Guid>();
private List<Guid> componentsToDeactivate = new List<Guid>(); private List<Guid> componentsToDeactivate = new List<Guid>();
private List<Guid> componentsToRemove = new List<Guid>(); private List<Guid> componentsToRemove = new List<Guid>();
//shared references with EntityManager
private List<Guid> entitiesWithAddedComponents;
private List<Guid> entitiesWithRemovedComponents;
public ComponentManager(
List<Guid> entitiesWithAddedComponents,
List<Guid> entitiesWithRemovedComponents
)
{
this.entitiesWithAddedComponents = entitiesWithAddedComponents;
this.entitiesWithRemovedComponents = entitiesWithRemovedComponents;
}
internal Guid AddComponent<TComponent>(Guid entityID, TComponent component) where TComponent : struct, IComponent internal Guid AddComponent<TComponent>(Guid entityID, TComponent component) where TComponent : struct, IComponent
{ {
var componentID = Guid.NewGuid(); var componentID = Guid.NewGuid();
@ -25,6 +40,13 @@ namespace Encompass
IDToComponent[componentID] = component; IDToComponent[componentID] = component;
componentIDToType[componentID] = typeof(TComponent); componentIDToType[componentID] = typeof(TComponent);
if (!typeToComponentIDs.ContainsKey(typeof(TComponent)))
{
typeToComponentIDs.Add(typeof(TComponent), new List<Guid>());
}
typeToComponentIDs[typeof(TComponent)].Add(componentID);
if (!entityIDToComponentIDs.ContainsKey(entityID)) if (!entityIDToComponentIDs.ContainsKey(entityID))
{ {
entityIDToComponentIDs.Add(entityID, new List<Guid>()); entityIDToComponentIDs.Add(entityID, new List<Guid>());
@ -33,33 +55,30 @@ namespace Encompass
entityIDToComponentIDs[entityID].Add(componentID); entityIDToComponentIDs[entityID].Add(componentID);
componentIDToEntityID[componentID] = entityID; componentIDToEntityID[componentID] = entityID;
if (!activeComponents.ContainsKey(typeof(TComponent))) inactiveComponents.Add(componentID);
{
activeComponents.Add(typeof(TComponent), new List<Guid>());
inactiveComponents.Add(typeof(TComponent), new List<Guid>());
}
MarkForActivation(componentID); MarkForActivation(componentID);
entitiesWithAddedComponents.Add(entityID);
return componentID; return componentID;
} }
internal IEnumerable<KeyValuePair<Guid, IComponent>> GetComponentsByEntity(Guid entityID) internal IEnumerable<KeyValuePair<Guid, IComponent>> GetComponentsByEntity(Guid entityID)
{ {
return entityIDToComponentIDs[entityID].Select((id) => new KeyValuePair<Guid, IComponent>(id, IDToComponent[id])); return entityIDToComponentIDs[entityID].Intersect(activeComponents).Select((id) => new KeyValuePair<Guid, IComponent>(id, IDToComponent[id]));
} }
internal IEnumerable<KeyValuePair<Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent internal IEnumerable<KeyValuePair<Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent
{ {
return activeComponents.ContainsKey(typeof(TComponent)) ? return typeToComponentIDs.ContainsKey(typeof(TComponent)) ?
activeComponents[typeof(TComponent)].Select((id) => new KeyValuePair<Guid, TComponent>(id, (TComponent)IDToComponent[id])) : typeToComponentIDs[typeof(TComponent)].Select((id) => new KeyValuePair<Guid, TComponent>(id, (TComponent)IDToComponent[id])) :
Enumerable.Empty<KeyValuePair<Guid, TComponent>>(); Enumerable.Empty<KeyValuePair<Guid, TComponent>>();
} }
internal IEnumerable<KeyValuePair<Guid, IComponent>> GetActiveComponentsByType(Type type) internal IEnumerable<KeyValuePair<Guid, IComponent>> GetActiveComponentsByType(Type type)
{ {
return activeComponents.ContainsKey(type) ? return typeToComponentIDs.ContainsKey(type) ?
activeComponents[type].Select((id) => new KeyValuePair<Guid, IComponent>(id, IDToComponent[id])) : typeToComponentIDs[type].Select((id) => new KeyValuePair<Guid, IComponent>(id, IDToComponent[id])) :
Enumerable.Empty<KeyValuePair<Guid, IComponent>>(); Enumerable.Empty<KeyValuePair<Guid, IComponent>>();
} }
@ -82,6 +101,11 @@ namespace Encompass
return entityComponents.Intersect(activeComponentsByType); return entityComponents.Intersect(activeComponentsByType);
} }
internal IEnumerable<Type> GetAllComponentTypesOfEntity(Guid entityID)
{
return entityIDToComponentIDs[entityID].Select((id) => componentIDToType[id]);
}
internal bool EntityHasComponentOfType<TComponent>(Guid entityID) where TComponent : struct, IComponent internal bool EntityHasComponentOfType<TComponent>(Guid entityID) where TComponent : struct, IComponent
{ {
return GetComponentsByEntityAndType<TComponent>(entityID).Any(); return GetComponentsByEntityAndType<TComponent>(entityID).Any();
@ -119,9 +143,7 @@ namespace Encompass
foreach (var componentID in componentIDs) foreach (var componentID in componentIDs)
{ {
var component = IDToComponent[componentID]; MarkForRemoval(componentID);
activeComponents[component.GetType()].Remove(componentID);
inactiveComponents[component.GetType()].Remove(componentID);
} }
entityIDToComponentIDs.Remove(entityID); entityIDToComponentIDs.Remove(entityID);
@ -147,8 +169,10 @@ namespace Encompass
foreach (var componentID in componentsToActivate) foreach (var componentID in componentsToActivate)
{ {
var component = IDToComponent[componentID]; var component = IDToComponent[componentID];
activeComponents[component.GetType()].Add(componentID); if (inactiveComponents.Remove(componentID))
inactiveComponents[component.GetType()].Remove(componentID); {
activeComponents.Add(componentID);
}
} }
componentsToActivate.Clear(); componentsToActivate.Clear();
@ -159,8 +183,10 @@ namespace Encompass
foreach (var componentID in componentsToDeactivate) foreach (var componentID in componentsToDeactivate)
{ {
var component = IDToComponent[componentID]; var component = IDToComponent[componentID];
activeComponents[component.GetType()].Remove(componentID); if (activeComponents.Remove(componentID))
inactiveComponents[component.GetType()].Add(componentID); {
inactiveComponents.Add(componentID);
}
} }
componentsToDeactivate.Clear(); componentsToDeactivate.Clear();
@ -171,8 +197,8 @@ namespace Encompass
foreach (var componentID in componentsToRemove) foreach (var componentID in componentsToRemove)
{ {
var component = IDToComponent[componentID]; var component = IDToComponent[componentID];
activeComponents[component.GetType()].Remove(componentID); activeComponents.Remove(componentID);
inactiveComponents[component.GetType()].Remove(componentID); inactiveComponents.Remove(componentID);
} }
componentsToRemove.Clear(); componentsToRemove.Clear();

View File

@ -18,7 +18,7 @@ namespace Encompass
public Guid AddComponent<TComponent>(TComponent component) where TComponent : struct, IComponent public Guid AddComponent<TComponent>(TComponent component) where TComponent : struct, IComponent
{ {
return componentManager.AddComponent<TComponent>(id, component); return componentManager.AddComponent(id, component);
} }
public IEnumerable<KeyValuePair<Guid, TComponent>> GetComponents<TComponent>() where TComponent : struct, IComponent public IEnumerable<KeyValuePair<Guid, TComponent>> GetComponents<TComponent>() where TComponent : struct, IComponent

View File

@ -5,23 +5,35 @@ namespace Encompass
{ {
internal class EntityManager internal class EntityManager
{ {
private List<Entity> entities = new List<Entity>();
private Dictionary<Guid, Entity> IDToEntity = new Dictionary<Guid, Entity>(); private Dictionary<Guid, Entity> IDToEntity = new Dictionary<Guid, Entity>();
private List<Entity> entitiesMarkedForDestroy = new List<Entity>(); private List<Guid> entitiesMarkedForDestroy = new List<Guid>();
private Dictionary<Type, HashSet<IEntityTracker>> componentTypeToEntityTrackers = new Dictionary<Type, HashSet<IEntityTracker>>();
private Dictionary<Guid, HashSet<IEntityTracker>> entityToEntityTrackers = new Dictionary<Guid, HashSet<IEntityTracker>>();
private List<Guid> entitiesWithAddedComponents;
private List<Guid> entitiesWithRemovedComponents;
private ComponentManager componentManager; private ComponentManager componentManager;
public EntityManager( public EntityManager(
ComponentManager componentManager ComponentManager componentManager,
List<Guid> entitiesWithAddedComponents,
List<Guid> entitiesWithRemovedComponents
) )
{ {
this.componentManager = componentManager; this.componentManager = componentManager;
this.entitiesWithAddedComponents = entitiesWithAddedComponents;
this.entitiesWithRemovedComponents = entitiesWithRemovedComponents;
} }
public Entity CreateEntity() 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) public Entity GetEntity(Guid id)
@ -29,22 +41,109 @@ namespace Encompass
return IDToEntity[id]; 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(); entity.RemoveAllComponents();
IDToEntity.Remove(entityID);
entityToEntityTrackers.Remove(entityID);
} }
entitiesMarkedForDestroy.Clear();
} }
private Guid NextID() private Guid NextID()
{ {
return Guid.NewGuid(); return Guid.NewGuid();
} }
public void RegisterEntityTracker(IEntityTracker entityTracker)
{
foreach (var componentType in entityTracker.ComponentTypes)
{
if (!componentTypeToEntityTrackers.ContainsKey(componentType))
{
componentTypeToEntityTrackers.Add(componentType, new HashSet<IEntityTracker>());
}
componentTypeToEntityTrackers[componentType].Add(entityTracker);
}
if (entityTracker is EntityRenderer)
{
var entityRenderer = entityTracker as EntityRenderer;
if (!componentTypeToEntityTrackers.ContainsKey(entityRenderer.DrawComponentType))
{
componentTypeToEntityTrackers.Add(entityRenderer.DrawComponentType, new HashSet<IEntityTracker>());
}
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<IEntityTracker>());
}
entityToEntityTrackers[entityID].Add(entityTracker);
}
}
}
}
}
} }
} }

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
public interface IEntityTracker
{
IEnumerable<Type> ComponentTypes { get; }
bool CheckAndTrackEntity(Guid entityID);
bool CheckAndUntrackEntity(Guid entityID);
}
}

View File

@ -34,9 +34,13 @@ namespace Encompass
messageManager.ClearMessages(); messageManager.ClearMessages();
entityManager.DestroyMarkedEntities(); entityManager.DestroyMarkedEntities();
componentManager.ActivateComponents(); componentManager.ActivateComponents();
componentManager.DeactivateComponents(); componentManager.DeactivateComponents();
componentManager.RemoveComponents(); componentManager.RemoveComponents();
entityManager.CheckEntitiesWithAddedComponents();
entityManager.CheckEntitiesWithRemovedComponents();
} }
public void Draw() public void Draw()

View File

@ -20,8 +20,10 @@ namespace Encompass
public WorldBuilder() public WorldBuilder()
{ {
componentManager = new ComponentManager(); var entitiesWithAddedComponents = new List<Guid>();
entityManager = new EntityManager(componentManager); var entitiesWithRemovedComponents = new List<Guid>();
componentManager = new ComponentManager(entitiesWithAddedComponents, entitiesWithRemovedComponents);
entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);
messageManager = new MessageManager(); messageManager = new MessageManager();
renderManager = new RenderManager(entityManager, componentManager); renderManager = new RenderManager(entityManager, componentManager);
} }
@ -43,6 +45,11 @@ namespace Encompass
engineGraph.AddVertex(engine); engineGraph.AddVertex(engine);
if (engine is IEntityTracker)
{
entityManager.RegisterEntityTracker(engine as IEntityTracker);
}
var emitMessageAttribute = engine.GetType().GetCustomAttribute<Emits>(false); var emitMessageAttribute = engine.GetType().GetCustomAttribute<Emits>(false);
if (emitMessageAttribute != null) if (emitMessageAttribute != null)
{ {
@ -98,6 +105,7 @@ namespace Encompass
if (renderer is EntityRenderer) if (renderer is EntityRenderer)
{ {
entityManager.RegisterEntityTracker(renderer as IEntityTracker);
renderManager.RegisterEntityRenderer(renderer as EntityRenderer); renderManager.RegisterEntityRenderer(renderer as EntityRenderer);
} }
else if (renderer is GeneralRenderer) else if (renderer is GeneralRenderer)
@ -182,8 +190,12 @@ namespace Encompass
renderManager renderManager
); );
this.componentManager.ActivateComponents(); componentManager.ActivateComponents();
this.componentManager.RemoveComponents(); componentManager.DeactivateComponents();
componentManager.RemoveComponents();
entityManager.CheckEntitiesWithAddedComponents();
entityManager.CheckEntitiesWithRemovedComponents();
return world; return world;
} }

View File

@ -1,18 +1,29 @@
using System; using System;
using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace Encompass namespace Encompass
{ {
public abstract class EntityRenderer : Renderer public abstract class EntityRenderer : Renderer, IEntityTracker
{ {
private readonly List<Type> componentTypes = new List<Type>(); private readonly List<Type> componentTypes = new List<Type>();
private readonly Type drawComponentType;
private EntityTracker entityTracker = new EntityTracker(); private EntityTracker entityTracker = new EntityTracker();
public IEnumerable<Type> ComponentTypes { get { return componentTypes; } }
public Type DrawComponentType { get; }
public abstract void Render(Entity entity); public abstract void Render(Entity entity);
public EntityRenderer()
{
var rendersAttribute = GetType().GetCustomAttribute<Renders>(false);
if (rendersAttribute != null)
{
componentTypes = rendersAttribute.componentTypes;
DrawComponentType = rendersAttribute.drawComponentType;
}
}
public bool CheckAndTrackEntity(Guid entityID) public bool CheckAndTrackEntity(Guid entityID)
{ {
var entity = GetEntity(entityID); var entity = GetEntity(entityID);
@ -37,7 +48,7 @@ namespace Encompass
internal bool CheckEntity(Entity entity) internal bool CheckEntity(Entity entity)
{ {
return EntityChecker.CheckEntity(entity, componentTypes) && return EntityChecker.CheckEntity(entity, componentTypes) &&
entity.HasComponent(drawComponentType); entity.HasComponent(DrawComponentType);
} }
} }
} }