refactor so entity and components use GUID
parent
e4705dc7ea
commit
7c787290eb
|
@ -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);
|
||||
var entityID = componentIDToEntityID[componentID];
|
||||
IDToComponent[componentID] = newComponentValue;
|
||||
}
|
||||
|
||||
if (inactiveComponents[originalComponent.GetType()].Remove(originalComponent))
|
||||
internal void RemoveAllComponentsFromEntity(Guid entityID)
|
||||
{
|
||||
inactiveComponents[originalComponent.GetType()].Add(newComponent);
|
||||
var componentIDs = entityIDToComponentIDs[entityID];
|
||||
|
||||
foreach (var componentID in componentIDs)
|
||||
{
|
||||
var component = IDToComponent[componentID];
|
||||
activeComponents[component.GetType()].Remove(componentID);
|
||||
inactiveComponents[component.GetType()].Remove(componentID);
|
||||
}
|
||||
|
||||
if (componentsToActivate.Remove(originalComponent))
|
||||
{
|
||||
componentsToActivate.Add(newComponent);
|
||||
entityIDToComponentIDs.Remove(entityID);
|
||||
}
|
||||
|
||||
if (componentsToDeactivate.Remove(originalComponent))
|
||||
internal void MarkForActivation(Guid componentID)
|
||||
{
|
||||
componentsToDeactivate.Add(newComponent);
|
||||
componentsToActivate.Add(componentID);
|
||||
}
|
||||
|
||||
if (componentsToRemove.Remove(originalComponent))
|
||||
internal void MarkForDeactivation(Guid componentID)
|
||||
{
|
||||
componentsToRemove.Add(newComponent);
|
||||
}
|
||||
componentsToDeactivate.Add(componentID);
|
||||
}
|
||||
|
||||
internal void RemoveAllComponentsFromEntity(uint entityID)
|
||||
internal void MarkForRemoval(Guid componentID)
|
||||
{
|
||||
var components = GetComponentsByEntity(entityID);
|
||||
|
||||
foreach (var component in components)
|
||||
{
|
||||
activeComponents[component.GetType()].Remove(component);
|
||||
inactiveComponents[component.GetType()].Remove(component);
|
||||
}
|
||||
|
||||
entityIDToComponents.Remove(entityID);
|
||||
}
|
||||
|
||||
internal void MarkForActivation(IComponent component)
|
||||
{
|
||||
componentsToActivate.Add(component);
|
||||
}
|
||||
|
||||
internal void MarkForDeactivation(IComponent component)
|
||||
{
|
||||
componentsToDeactivate.Add(component);
|
||||
}
|
||||
|
||||
internal void MarkForRemoval(IComponent component)
|
||||
{
|
||||
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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue