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,19 +11,41 @@ namespace Encompass
private Dictionary<Guid, List<Guid>> entityIDToComponentIDs = new Dictionary<Guid, List<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>> inactiveComponents = new Dictionary<Type, List<Guid>>();
private Dictionary<Type, List<Guid>> typeToComponentIDs = 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> componentsToDeactivate = 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
{
var componentID = Guid.NewGuid();
IDToComponent[componentID] = component;
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))
{
@ -33,33 +55,30 @@ namespace Encompass
entityIDToComponentIDs[entityID].Add(componentID);
componentIDToEntityID[componentID] = entityID;
if (!activeComponents.ContainsKey(typeof(TComponent)))
{
activeComponents.Add(typeof(TComponent), new List<Guid>());
inactiveComponents.Add(typeof(TComponent), new List<Guid>());
}
inactiveComponents.Add(componentID);
MarkForActivation(componentID);
entitiesWithAddedComponents.Add(entityID);
return componentID;
}
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
{
return activeComponents.ContainsKey(typeof(TComponent)) ?
activeComponents[typeof(TComponent)].Select((id) => new KeyValuePair<Guid, TComponent>(id, (TComponent)IDToComponent[id])) :
return typeToComponentIDs.ContainsKey(typeof(TComponent)) ?
typeToComponentIDs[typeof(TComponent)].Select((id) => new KeyValuePair<Guid, TComponent>(id, (TComponent)IDToComponent[id])) :
Enumerable.Empty<KeyValuePair<Guid, TComponent>>();
}
internal IEnumerable<KeyValuePair<Guid, IComponent>> GetActiveComponentsByType(Type type)
{
return activeComponents.ContainsKey(type) ?
activeComponents[type].Select((id) => new KeyValuePair<Guid, IComponent>(id, IDToComponent[id])) :
return typeToComponentIDs.ContainsKey(type) ?
typeToComponentIDs[type].Select((id) => new KeyValuePair<Guid, IComponent>(id, IDToComponent[id])) :
Enumerable.Empty<KeyValuePair<Guid, IComponent>>();
}
@ -82,6 +101,11 @@ namespace Encompass
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
{
return GetComponentsByEntityAndType<TComponent>(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();

View File

@ -18,7 +18,7 @@ namespace Encompass
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

View File

@ -5,23 +5,35 @@ namespace Encompass
{
internal class EntityManager
{
private List<Entity> entities = new List<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;
public EntityManager(
ComponentManager componentManager
ComponentManager componentManager,
List<Guid> entitiesWithAddedComponents,
List<Guid> 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<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();
entityManager.DestroyMarkedEntities();
componentManager.ActivateComponents();
componentManager.DeactivateComponents();
componentManager.RemoveComponents();
entityManager.CheckEntitiesWithAddedComponents();
entityManager.CheckEntitiesWithRemovedComponents();
}
public void Draw()

View File

@ -20,8 +20,10 @@ namespace Encompass
public WorldBuilder()
{
componentManager = new ComponentManager();
entityManager = new EntityManager(componentManager);
var entitiesWithAddedComponents = new List<Guid>();
var entitiesWithRemovedComponents = new List<Guid>();
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<Emits>(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;
}

View File

@ -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<Type> componentTypes = new List<Type>();
private readonly Type drawComponentType;
private EntityTracker entityTracker = new EntityTracker();
public IEnumerable<Type> ComponentTypes { get { return componentTypes; } }
public Type DrawComponentType { get; }
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)
{
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);
}
}
}