refactor so entity and components use GUID

pull/5/head
Evan Hemsley 2019-06-17 11:33:38 -07:00
parent e4705dc7ea
commit 7c787290eb
6 changed files with 124 additions and 145 deletions

View File

@ -6,133 +6,111 @@ namespace Encompass
{
internal class ComponentManager
{
private Dictionary<uint, List<IComponent>> entityIDToComponents = new Dictionary<uint, List<IComponent>>();
private Dictionary<IComponent, uint> componentToEntityID = new Dictionary<IComponent, uint>();
private Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>();
private Dictionary<Guid, List<Guid>> entityIDToComponentIDs = new Dictionary<Guid, List<Guid>>();
private Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
private Dictionary<Type, List<IComponent>> activeComponents = new Dictionary<Type, List<IComponent>>();
private Dictionary<Type, List<IComponent>> inactiveComponents = new Dictionary<Type, List<IComponent>>();
private Dictionary<Type, List<Guid>> activeComponents = new Dictionary<Type, List<Guid>>();
private Dictionary<Type, List<Guid>> inactiveComponents = new Dictionary<Type, List<Guid>>();
private List<IComponent> componentsToActivate = new List<IComponent>();
private List<IComponent> componentsToDeactivate = new List<IComponent>();
private List<IComponent> componentsToRemove = new List<IComponent>();
private List<Guid> componentsToActivate = new List<Guid>();
private List<Guid> componentsToDeactivate = new List<Guid>();
private List<Guid> componentsToRemove = new List<Guid>();
internal void AddComponent<TComponent>(uint entityID, TComponent component) where TComponent : struct, IComponent
internal Guid AddComponent<TComponent>(Guid entityID, TComponent component) where TComponent : struct, IComponent
{
if (!entityIDToComponents.ContainsKey(entityID))
var componentID = Guid.NewGuid();
IDToComponent[componentID] = component;
if (!entityIDToComponentIDs.ContainsKey(entityID))
{
entityIDToComponents.Add(entityID, new List<IComponent>());
entityIDToComponentIDs.Add(entityID, new List<Guid>());
}
entityIDToComponents[entityID].Add(component);
componentToEntityID[component] = entityID;
entityIDToComponentIDs[entityID].Add(componentID);
componentIDToEntityID[componentID] = entityID;
if (!activeComponents.ContainsKey(typeof(TComponent)))
{
activeComponents.Add(typeof(TComponent), new List<IComponent>());
inactiveComponents.Add(typeof(TComponent), new List<IComponent>());
activeComponents.Add(typeof(TComponent), new List<Guid>());
inactiveComponents.Add(typeof(TComponent), new List<Guid>());
}
MarkForActivation(component);
MarkForActivation(componentID);
return componentID;
}
internal IEnumerable<IComponent> GetComponentsByEntity(uint entityID)
internal IEnumerable<KeyValuePair<Guid, IComponent>> GetComponentsByEntity(Guid entityID)
{
return entityIDToComponents[entityID];
return entityIDToComponentIDs[entityID].Select((id) => new KeyValuePair<Guid, IComponent>(id, IDToComponent[id]));
}
internal IEnumerable<TComponent> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent
internal IEnumerable<KeyValuePair<Guid, TComponent>> GetActiveComponentsByType<TComponent>() where TComponent : struct, IComponent
{
return activeComponents[typeof(TComponent)].Cast<TComponent>();
return activeComponents[typeof(TComponent)].Select((id) => new KeyValuePair<Guid, TComponent>(id, (TComponent)IDToComponent[id]));
}
internal TComponent GetActiveComponentByType<TComponent>() where TComponent : struct, IComponent
internal KeyValuePair<Guid, TComponent> GetActiveComponentByType<TComponent>() where TComponent : struct, IComponent
{
return GetActiveComponentsByType<TComponent>().Single();
}
internal IEnumerable<TComponent> GetComponentsByEntityAndType<TComponent>(uint entityID) where TComponent : struct, IComponent
internal IEnumerable<KeyValuePair<Guid, TComponent>> GetComponentsByEntityAndType<TComponent>(Guid entityID) where TComponent : struct, IComponent
{
var entity_components = GetComponentsByEntity(entityID).Cast<TComponent>();
var entity_components = GetComponentsByEntity(entityID).Select((kv) => new KeyValuePair<Guid, TComponent>(kv.Key, (TComponent)kv.Value));
var active_components_by_type = GetActiveComponentsByType<TComponent>();
return entity_components.Intersect(active_components_by_type).Cast<TComponent>();
return entity_components.Intersect(active_components_by_type);
}
internal bool EntityHasComponentOfType<TComponent>(uint entityID) where TComponent : struct, IComponent
internal bool EntityHasComponentOfType<TComponent>(Guid entityID) where TComponent : struct, IComponent
{
return GetComponentsByEntityAndType<TComponent>(entityID).Any();
}
/** Replaces the component with another. */
internal void UpdateComponent<TComponent>(TComponent originalComponent, TComponent newComponent) where TComponent : struct, IComponent
internal void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
{
var entityID = componentToEntityID[originalComponent];
entityIDToComponents[entityID].Remove(originalComponent);
entityIDToComponents[entityID].Add(newComponent);
componentToEntityID.Remove(originalComponent);
componentToEntityID.Add(newComponent, entityID);
if (activeComponents[originalComponent.GetType()].Remove(originalComponent))
{
activeComponents[originalComponent.GetType()].Add(newComponent);
}
if (inactiveComponents[originalComponent.GetType()].Remove(originalComponent))
{
inactiveComponents[originalComponent.GetType()].Add(newComponent);
}
if (componentsToActivate.Remove(originalComponent))
{
componentsToActivate.Add(newComponent);
}
if (componentsToDeactivate.Remove(originalComponent))
{
componentsToDeactivate.Add(newComponent);
}
if (componentsToRemove.Remove(originalComponent))
{
componentsToRemove.Add(newComponent);
}
var entityID = componentIDToEntityID[componentID];
IDToComponent[componentID] = newComponentValue;
}
internal void RemoveAllComponentsFromEntity(uint entityID)
internal void RemoveAllComponentsFromEntity(Guid entityID)
{
var components = GetComponentsByEntity(entityID);
var componentIDs = entityIDToComponentIDs[entityID];
foreach (var component in components)
foreach (var componentID in componentIDs)
{
activeComponents[component.GetType()].Remove(component);
inactiveComponents[component.GetType()].Remove(component);
var component = IDToComponent[componentID];
activeComponents[component.GetType()].Remove(componentID);
inactiveComponents[component.GetType()].Remove(componentID);
}
entityIDToComponents.Remove(entityID);
entityIDToComponentIDs.Remove(entityID);
}
internal void MarkForActivation(IComponent component)
internal void MarkForActivation(Guid componentID)
{
componentsToActivate.Add(component);
componentsToActivate.Add(componentID);
}
internal void MarkForDeactivation(IComponent component)
internal void MarkForDeactivation(Guid componentID)
{
componentsToDeactivate.Add(component);
componentsToDeactivate.Add(componentID);
}
internal void MarkForRemoval(IComponent component)
internal void MarkForRemoval(Guid componentID)
{
componentsToRemove.Add(component);
componentsToRemove.Add(componentID);
}
internal void ActivateComponents()
{
foreach (var component in componentsToActivate)
foreach (var componentID in componentsToActivate)
{
activeComponents[component.GetType()].Add(component);
inactiveComponents[component.GetType()].Remove(component);
var component = IDToComponent[componentID];
activeComponents[component.GetType()].Add(componentID);
inactiveComponents[component.GetType()].Remove(componentID);
}
componentsToActivate.Clear();
@ -140,19 +118,23 @@ namespace Encompass
internal void DeactivateComponents()
{
foreach (var component in componentsToDeactivate)
foreach (var componentID in componentsToDeactivate)
{
activeComponents[component.GetType()].Remove(component);
inactiveComponents[component.GetType()].Add(component);
var component = IDToComponent[componentID];
activeComponents[component.GetType()].Remove(componentID);
inactiveComponents[component.GetType()].Add(componentID);
}
componentsToDeactivate.Clear();
}
internal void RemoveComponents()
{
foreach (var component in componentsToRemove)
foreach (var componentID in componentsToRemove)
{
activeComponents[component.GetType()].Remove(component);
inactiveComponents[component.GetType()].Remove(component);
var component = IDToComponent[componentID];
activeComponents[component.GetType()].Remove(componentID);
inactiveComponents[component.GetType()].Remove(componentID);
}
componentsToRemove.Clear();

View File

@ -58,21 +58,21 @@ namespace Encompass
return this.entityManager.CreateEntity();
}
protected IEnumerable<TComponent> ReadComponents<TComponent>() where TComponent : struct, IComponent
protected IEnumerable<KeyValuePair<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
{
return this.componentManager.GetActiveComponentsByType<TComponent>();
}
protected TComponent ReadComponent<TComponent>() where TComponent : struct, IComponent
protected KeyValuePair<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent
{
return this.componentManager.GetActiveComponentByType<TComponent>();
}
internal void UpdateComponentInWorld<TComponent>(TComponent originalComponent, TComponent newComponent) where TComponent : struct, IComponent
internal void UpdateComponentInWorld<TComponent>(Guid componentID, TComponent newComponent) where TComponent : struct, IComponent
{
if (mutateComponentTypes.Contains(typeof(TComponent)))
{
this.componentManager.UpdateComponent(originalComponent, newComponent);
this.componentManager.UpdateComponent(componentID, newComponent);
}
else
{
@ -80,18 +80,9 @@ namespace Encompass
}
}
protected void UpdateComponent<TComponent>(TComponent component, Func<TComponent, TComponent> updateFunction) where TComponent : struct, IComponent
protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
{
var updatedComponent = updateFunction(component);
this.UpdateComponentInWorld(component, updatedComponent);
}
protected void UpdateComponents<TComponent>(IEnumerable<TComponent> components, Func<TComponent, TComponent> updateFunction) where TComponent : struct, IComponent
{
foreach (var component in components)
{
this.UpdateComponent(component, updateFunction);
}
this.UpdateComponentInWorld(componentID, newComponentValue);
}
protected void EmitMessage<TMessage>(TMessage message) where TMessage : struct, IMessage

View File

@ -1,31 +1,32 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
public struct Entity
{
public readonly uint id;
public readonly Guid id;
private ComponentManager componentManager;
internal Entity(uint id, ComponentManager componentManager)
internal Entity(Guid id, ComponentManager componentManager)
{
this.id = id;
this.componentManager = componentManager;
}
public void AddComponent<TComponent>(TComponent component) where TComponent : struct, IComponent
public Guid AddComponent<TComponent>(TComponent component) where TComponent : struct, IComponent
{
componentManager.AddComponent<TComponent>(id, component);
return componentManager.AddComponent<TComponent>(id, component);
}
public IEnumerable<TComponent> GetComponents<TComponent>() where TComponent : struct, IComponent
public IEnumerable<KeyValuePair<Guid, TComponent>> GetComponents<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetComponentsByEntityAndType<TComponent>(id);
}
public TComponent GetComponent<TComponent>() where TComponent : struct, IComponent
public KeyValuePair<Guid, TComponent> GetComponent<TComponent>() where TComponent : struct, IComponent
{
return GetComponents<TComponent>().First();
}

View File

@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
internal class EntityManager
{
private uint nextID = 1;
private List<Entity> entities = new List<Entity>();
private Dictionary<uint, Entity> IDToEntity = new Dictionary<uint, Entity>();
private Dictionary<Guid, Entity> IDToEntity = new Dictionary<Guid, Entity>();
private List<Entity> entitiesMarkedForDestroy = new List<Entity>();
@ -25,7 +24,7 @@ namespace Encompass
return new Entity(NextID(), componentManager);
}
public Entity GetEntity(uint id)
public Entity GetEntity(Guid id)
{
return this.IDToEntity[id];
}
@ -43,11 +42,9 @@ namespace Encompass
}
}
private uint NextID()
private Guid NextID()
{
var id = this.nextID;
this.nextID++;
return id;
return Guid.NewGuid();
}
}
}

View File

@ -1,15 +1,17 @@
using NUnit.Framework;
using System.Linq;
using FluentAssertions;
using Encompass;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Collections.Generic;
namespace Tests
{
public class EngineTest
{
static IEnumerable<MockComponent> resultComponents;
static List<KeyValuePair<Guid, MockComponent>> resultComponents;
static MockComponent resultComponent;
static List<MockMessage> resultMessages;
@ -18,7 +20,7 @@ namespace Tests
{
public override void Update(float dt)
{
resultComponents = this.ReadComponents<MockComponent>();
resultComponents = this.ReadComponents<MockComponent>().ToList();
}
}
@ -26,7 +28,7 @@ namespace Tests
{
public override void Update(float dt)
{
resultComponent = this.ReadComponent<MockComponent>();
resultComponent = this.ReadComponent<MockComponent>().Value;
}
}
@ -46,15 +48,16 @@ namespace Tests
mockComponentB.myInt = 1;
mockComponentB.myString = "howdy";
entity.AddComponent(mockComponent);
entity.AddComponent(mockComponentB);
var componentAID = entity.AddComponent(mockComponent);
var componentBID = entity.AddComponent(mockComponentB);
var world = worldBuilder.Build();
world.Update(0.01f);
Assert.Contains(mockComponent, resultComponents.ToList());
Assert.Contains(mockComponentB, resultComponents.ToList());
var resultComponentValues = resultComponents.Select((kv) => kv.Value);
resultComponentValues.Should().Contain(mockComponent);
resultComponentValues.Should().Contain(mockComponentB);
}
[Test]
@ -107,14 +110,13 @@ namespace Tests
{
public override void Update(float dt)
{
var component = this.ReadComponent<MockComponent>();
this.UpdateComponent(component, (MockComponent comp) =>
{
comp.myInt = 420;
comp.myString = "blaze it";
return comp;
});
resultComponent = this.ReadComponent<MockComponent>();
(var componentID, var component) = this.ReadComponent<MockComponent>();
component.myInt = 420;
component.myString = "blaze it";
this.UpdateComponent(componentID, component);
resultComponent = this.ReadComponent<MockComponent>().Value;
}
}
@ -144,14 +146,13 @@ namespace Tests
{
public override void Update(float dt)
{
var component = this.ReadComponent<MockComponent>();
this.UpdateComponent(component, (MockComponent comp) =>
{
comp.myInt = 420;
comp.myString = "blaze it";
return comp;
});
component = this.ReadComponent<MockComponent>();
(var componentID, var component) = this.ReadComponent<MockComponent>();
component.myInt = 420;
component.myString = "blaze it";
this.UpdateComponent(componentID, component);
component = this.ReadComponent<MockComponent>().Value;
}
}

View File

@ -1,10 +1,16 @@
using NUnit.Framework;
using FluentAssertions;
using System.Linq;
using Encompass;
using System.Collections.Generic;
using System;
namespace Tests {
struct MockComponent : IComponent {
namespace Tests
{
struct MockComponent : IComponent
{
public string myString;
public int myInt;
}
@ -27,7 +33,7 @@ namespace Tests {
// world.Update();
Assert.IsTrue(entity.HasComponent<MockComponent>());
Assert.AreEqual(mockComponent, entity.GetComponent<MockComponent>());
Assert.That(entity.GetComponent<MockComponent>().Value, Is.EqualTo(mockComponent));
}
[Test]
@ -48,15 +54,16 @@ namespace Tests {
mockComponentC.myInt = 1;
mockComponentC.myString = "howdy";
entity.AddComponent<MockComponent>(mockComponentA);
entity.AddComponent<MockComponent>(mockComponentB);
entity.AddComponent<MockComponent>(mockComponentC);
var componentAID = entity.AddComponent<MockComponent>(mockComponentA);
var componentBID = entity.AddComponent<MockComponent>(mockComponentB);
var componentCID = entity.AddComponent<MockComponent>(mockComponentC);
var world = worldBuilder.Build();
Assert.Contains(mockComponentA, entity.GetComponents<MockComponent>().ToList());
Assert.Contains(mockComponentB, entity.GetComponents<MockComponent>().ToList());
Assert.Contains(mockComponentC, entity.GetComponents<MockComponent>().ToList());
var components = entity.GetComponents<MockComponent>();
components.Should().Contain(new KeyValuePair<Guid, MockComponent>(componentAID, mockComponentA));
components.Should().Contain(new KeyValuePair<Guid, MockComponent>(componentBID, mockComponentB));
components.Should().Contain(new KeyValuePair<Guid, MockComponent>(componentCID, mockComponentC));
}
[Test]
@ -69,11 +76,11 @@ namespace Tests {
mockComponent.myInt = 3;
mockComponent.myString = "hello";
entity.AddComponent<MockComponent>(mockComponent);
var componentID = entity.AddComponent<MockComponent>(mockComponent);
var world = worldBuilder.Build();
Assert.AreEqual(mockComponent, entity.GetComponent<MockComponent>());
Assert.AreEqual(new KeyValuePair<Guid, MockComponent>(componentID, mockComponent), entity.GetComponent<MockComponent>());
}
[Test]