component updates at end of frame

pull/5/head
thatcosmonaut 2019-07-17 18:53:31 -07:00
parent ca82fa82cf
commit a62153730f
6 changed files with 85 additions and 7 deletions

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Encompass.Exceptions;
namespace Encompass namespace Encompass
{ {
@ -22,6 +23,8 @@ namespace Encompass
private readonly HashSet<Guid> componentsMarkedForDeactivation = new HashSet<Guid>(); private readonly HashSet<Guid> componentsMarkedForDeactivation = new HashSet<Guid>();
private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>(); private readonly HashSet<Guid> componentsMarkedForRemoval = new HashSet<Guid>();
private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>();
//shared references with EntityManager //shared references with EntityManager
private readonly HashSet<Guid> entitiesWithAddedComponents; private readonly HashSet<Guid> entitiesWithAddedComponents;
private readonly HashSet<Guid> entitiesWithRemovedComponents; private readonly HashSet<Guid> entitiesWithRemovedComponents;
@ -149,9 +152,24 @@ namespace Encompass
return componentIDToEntityID[componentID]; return componentIDToEntityID[componentID];
} }
internal void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent internal void AddUpdateComponentOperation<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
{ {
IDToComponent[componentID] = newComponentValue; if (pendingUpdates.ContainsKey(componentID))
{
throw new RepeatUpdateComponentException("Component with ID {0} was updated multiple times this frame", componentID);
}
pendingUpdates.Add(componentID, newComponentValue);
}
internal void PerformComponentUpdates()
{
foreach (var idPair in pendingUpdates)
{
IDToComponent[idPair.Key] = idPair.Value;
}
pendingUpdates.Clear();
} }
internal void MarkAllComponentsOnEntityForRemoval(Guid entityID) internal void MarkAllComponentsOnEntityForRemoval(Guid entityID)

View File

@ -134,7 +134,7 @@ namespace Encompass
throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name); throw new IllegalWriteException("Engine {0} tried to write undeclared Component {1}", this.GetType().Name, typeof(TComponent).Name);
} }
componentManager.UpdateComponent(componentID, newComponent); componentManager.AddUpdateComponentOperation(componentID, newComponent);
} }
protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent protected void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent

View File

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

View File

@ -35,6 +35,7 @@ namespace Encompass
messageManager.ClearMessages(); messageManager.ClearMessages();
entityManager.DestroyMarkedEntities(); entityManager.DestroyMarkedEntities();
componentManager.PerformComponentUpdates();
componentManager.ActivateMarkedComponents(); componentManager.ActivateMarkedComponents();
componentManager.DeactivateMarkedComponents(); componentManager.DeactivateMarkedComponents();
componentManager.RemoveMarkedComponents(); componentManager.RemoveMarkedComponents();

View File

@ -224,6 +224,7 @@ namespace Encompass
renderManager renderManager
); );
componentManager.PerformComponentUpdates();
componentManager.ActivateMarkedComponents(); componentManager.ActivateMarkedComponents();
componentManager.DeactivateMarkedComponents(); componentManager.DeactivateMarkedComponents();
componentManager.RemoveMarkedComponents(); componentManager.RemoveMarkedComponents();

View File

@ -31,6 +31,7 @@ namespace Tests
} }
} }
public class ReadComponentTestEngine : Engine public class ReadComponentTestEngine : Engine
{ {
public override void Update(double dt) public override void Update(double dt)
@ -126,16 +127,17 @@ namespace Tests
component.myInt = 420; component.myInt = 420;
component.myString = "blaze it"; component.myString = "blaze it";
UpdateComponent(componentID, component); UpdateComponent(componentID, component);
}
}
resultComponent = ReadComponent<MockComponent>().Item2; // this test needs to be improved...
}
}
[Test] [Test]
public void UpdateComponent() public void UpdateComponent()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new UpdateComponentTestEngine()); worldBuilder.AddEngine(new UpdateComponentTestEngine());
worldBuilder.AddEngine(new ReadComponentTestEngine());
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
@ -147,7 +149,8 @@ namespace Tests
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01f); world.Update(0.01);
world.Update(0.01);
Assert.AreEqual(420, resultComponent.myInt); Assert.AreEqual(420, resultComponent.myInt);
Assert.AreEqual("blaze it", resultComponent.myString); Assert.AreEqual("blaze it", resultComponent.myString);
@ -642,5 +645,48 @@ namespace Tests
Assert.Throws<ComponentAndMessageWriteException>(() => worldBuilder.AddEngine(new EngineThatWritesComponentAndMessage())); Assert.Throws<ComponentAndMessageWriteException>(() => worldBuilder.AddEngine(new EngineThatWritesComponentAndMessage()));
} }
struct MockComponentUpdateMessage : IMessage
{
public Guid componentID;
public MockComponent mockComponent;
}
[Reads(typeof(MockComponentUpdateMessage))]
[Writes(typeof(MockComponent))]
class RepeatUpdateEngine : Engine
{
public override void Update(double dt)
{
foreach (var mockComponentUpdateMessage in ReadMessages<MockComponentUpdateMessage>())
{
UpdateComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent);
UpdateComponent(mockComponentUpdateMessage.componentID, mockComponentUpdateMessage.mockComponent);
}
}
}
[Test]
public void EngineUpdatesComponentMultipleTimes()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEngine(new RepeatUpdateEngine());
var entity = worldBuilder.CreateEntity();
MockComponent mockComponent;
mockComponent.myInt = 1;
mockComponent.myString = "5";
var mockComponentID = worldBuilder.AddComponent(entity, mockComponent);
MockComponentUpdateMessage mockComponentUpdateMessage;
mockComponentUpdateMessage.componentID = mockComponentID;
mockComponentUpdateMessage.mockComponent = mockComponent;
worldBuilder.EmitMessage(mockComponentUpdateMessage);
var world = worldBuilder.Build();
Assert.Throws<RepeatUpdateComponentException>(() => world.Update(0.01));
}
} }
} }