component update mechanism

pull/5/head
Evan Hemsley 2019-06-15 00:39:08 -07:00
parent 4b9610762a
commit cb872b7c42
6 changed files with 158 additions and 0 deletions

View File

@ -5,6 +5,7 @@ using System.Linq;
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<Type, List<IComponent>> activeComponents = new Dictionary<Type, List<IComponent>>();
private Dictionary<Type, List<IComponent>> inactiveComponents = new Dictionary<Type, List<IComponent>>();
@ -19,6 +20,7 @@ namespace Encompass {
}
entityIDToComponents[entityID].Add(component);
componentToEntityID[component] = entityID;
if (!activeComponents.ContainsKey(typeof(TComponent))) {
activeComponents.Add(typeof(TComponent), new List<IComponent>());
@ -50,6 +52,37 @@ namespace Encompass {
return GetComponentsByEntityAndType<TComponent>(entityID).Any();
}
/** Replaces the component with another. */
internal void UpdateComponent<TComponent>(TComponent originalComponent, TComponent newComponent) 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);
}
}
internal void RemoveAllComponentsFromEntity(uint entityID) {
var components = GetComponentsByEntity(entityID);
@ -65,6 +98,10 @@ namespace Encompass {
componentsToActivate.Add(component);
}
internal void MarkForDeactivation(IComponent component) {
componentsToDeactivate.Add(component);
}
internal void MarkForRemoval(IComponent component) {
componentsToRemove.Add(component);
}
@ -78,6 +115,13 @@ namespace Encompass {
componentsToActivate.Clear();
}
internal void DeactivateComponents() {
foreach (var component in componentsToDeactivate) {
activeComponents[component.GetType()].Remove(component);
inactiveComponents[component.GetType()].Add(component);
}
}
internal void RemoveComponents() {
foreach (var component in componentsToRemove) {
activeComponents[component.GetType()].Remove(component);

View File

@ -1,10 +1,21 @@
using System;
using System.Reflection;
using System.Collections.Generic;
namespace Encompass {
public abstract class Engine {
public readonly List<Type> mutateComponentTypes = new List<Type>();
private EntityManager entityManager;
private ComponentManager componentManager;
public Engine() {
var mutatesAttribute = this.GetType().GetCustomAttribute<Mutates>(false);
if (mutatesAttribute != null) {
mutateComponentTypes = mutatesAttribute.mutateComponentTypes;
}
}
internal void AssignEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
@ -26,5 +37,13 @@ namespace Encompass {
protected TComponent ReadComponent<TComponent>() where TComponent : struct, IComponent {
return this.componentManager.GetActiveComponentByType<TComponent>();
}
protected void UpdateComponent<TComponent>(TComponent originalComponent, TComponent newComponent) where TComponent : struct, IComponent {
if (mutateComponentTypes.Contains(typeof(TComponent))) {
this.componentManager.UpdateComponent(originalComponent, newComponent);
} else {
throw new IllegalComponentMutationException("Engine {0} tried to mutate undeclared Component {1}", this.GetType().ToString(), typeof(TComponent).ToString());
}
}
}
}

15
src/attributes/Mutates.cs Normal file
View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace Encompass {
[System.AttributeUsage(System.AttributeTargets.Class)]
public class Mutates : System.Attribute
{
public readonly List<Type> mutateComponentTypes;
public Mutates(params Type[] mutateComponentTypes)
{
this.mutateComponentTypes = new List<Type>(mutateComponentTypes);
}
}
}

View File

@ -13,5 +13,7 @@
<Content Include="ComponentManager.cs" />
<Content Include="WorldBuilder.cs" />
<Content Include="Engine.cs" />
<Content Include="attributes\Mutates.cs" />
<Content Include="exceptions\IllegalComponentMutationException.cs" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,10 @@
using System;
namespace Encompass {
public class IllegalComponentMutationException : Exception {
public IllegalComponentMutationException(
string format,
params object[] args
) : base(string.Format(format, args)) { }
}
}

View File

@ -98,5 +98,73 @@ namespace Tests {
Assert.Throws<InvalidOperationException>(() => world.Update(0.01f));
}
[Mutates(typeof(MockComponent))]
public class UpdateComponentTestEngine : Engine
{
public override void Update(float dt)
{
var originalComponent = this.ReadComponent<MockComponent>();
var newComponent = this.ReadComponent<MockComponent>();
newComponent.myInt = 420;
newComponent.myString = "blaze it";
this.UpdateComponent(originalComponent, newComponent);
component = this.ReadComponent<MockComponent>();
}
}
[Test]
public void UpdateComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine<UpdateComponentTestEngine>();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
entity.AddComponent(mockComponent);
var world = worldBuilder.Build();
world.Update(0.01f);
Assert.AreEqual(420, component.myInt);
Assert.AreEqual("blaze it", component.myString);
}
public class UndeclaredUpdateComponentTestEngine : Engine
{
public override void Update(float dt)
{
var originalComponent = this.ReadComponent<MockComponent>();
var newComponent = this.ReadComponent<MockComponent>();
newComponent.myInt = 420;
newComponent.myString = "blaze it";
this.UpdateComponent(originalComponent, newComponent);
component = this.ReadComponent<MockComponent>();
}
}
[Test]
public void UpdateUndeclaredComponent()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine<UndeclaredUpdateComponentTestEngine>();
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 0;
mockComponent.myString = "hello";
entity.AddComponent(mockComponent);
var world = worldBuilder.Build();
Assert.Throws<IllegalComponentMutationException>(() => world.Update(0.01f));
}
}
}