From b4347f40854b2637f0d7fdad64581e4be370b9ba Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Mon, 24 Jun 2019 12:14:37 -0700 Subject: [PATCH] implement Detector and change World and Engine update to use double --- encompass-cs/Attributes/Detects.cs | 16 +++ encompass-cs/Engine.cs | 3 +- encompass-cs/Engines/Detector.cs | 59 +++++++++ .../DetectorWithoutComponentTypesException.cs | 12 ++ encompass-cs/World.cs | 2 +- encompass-cs/WorldBuilder.cs | 7 +- encompass-cs/attributes/Renders.cs | 1 - .../exceptions/EngineCycleException.cs | 2 +- .../EngineMutationConflictException.cs | 2 +- .../IllegalComponentMutationException.cs | 2 +- .../exceptions/IllegalMessageEmitException.cs | 2 +- .../exceptions/IllegalMessageReadException.cs | 2 +- test/DetectorTest.cs | 125 ++++++++++++++++++ test/EngineTest.cs | 33 ++--- test/WorldBuilderTest.cs | 25 ++-- 15 files changed, 254 insertions(+), 39 deletions(-) create mode 100644 encompass-cs/Attributes/Detects.cs create mode 100644 encompass-cs/Engines/Detector.cs create mode 100644 encompass-cs/Exceptions/DetectorWithoutComponentTypesException.cs create mode 100644 test/DetectorTest.cs diff --git a/encompass-cs/Attributes/Detects.cs b/encompass-cs/Attributes/Detects.cs new file mode 100644 index 0000000..533de18 --- /dev/null +++ b/encompass-cs/Attributes/Detects.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace Encompass +{ + [AttributeUsage(AttributeTargets.Class)] + public class Detects : Attribute + { + public readonly List componentTypes; + + public Detects(params Type[] componentTypes) + { + this.componentTypes = new List(componentTypes); + } + } +} diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 5853d61..29542cc 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -2,6 +2,7 @@ using System; using System.Reflection; using System.Collections.Generic; using System.Linq; +using Encompass.Exceptions; namespace Encompass { @@ -51,7 +52,7 @@ namespace Encompass this.messageManager = messageManager; } - public abstract void Update(float dt); + public abstract void Update(double dt); protected Entity CreateEntity() { diff --git a/encompass-cs/Engines/Detector.cs b/encompass-cs/Engines/Detector.cs new file mode 100644 index 0000000..4cbd64c --- /dev/null +++ b/encompass-cs/Engines/Detector.cs @@ -0,0 +1,59 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using Encompass.Exceptions; + +namespace Encompass.Engines +{ + public abstract class Detector : Engine, IEntityTracker + { + private readonly List componentTypes = new List(); + private readonly EntityTracker entityTracker = new EntityTracker(); + + public IEnumerable ComponentTypes { get { return componentTypes; } } + + protected Detector() + { + var detectsAttribute = GetType().GetCustomAttribute(false); + if (detectsAttribute != null) + { + componentTypes = detectsAttribute.componentTypes; + } + else + { + throw new DetectorWithoutComponentTypesException("Detector {0} does not have any component types declared. Use the Detects attribute to declare component types", GetType().Name); + } + } + + public override void Update(double dt) + { + foreach (var id in entityTracker.TrackedEntityIDs) + { + Detect(GetEntity(id), dt); + } + } + + public abstract void Detect(Entity entity, double dt); + + public bool CheckAndTrackEntity(Guid entityID) + { + var entity = GetEntity(entityID); + var shouldTrack = CheckEntity(entity); + if (shouldTrack) { entityTracker.TrackEntity(entityID); } + return shouldTrack; + } + + public bool CheckAndUntrackEntity(Guid entityID) + { + var entity = GetEntity(entityID); + var shouldUntrack = !CheckEntity(entity); + if (shouldUntrack) { entityTracker.UntrackEntity(entityID); } + return shouldUntrack; + } + + private bool CheckEntity(Entity entity) + { + return EntityChecker.CheckEntity(entity, componentTypes); + } + } +} diff --git a/encompass-cs/Exceptions/DetectorWithoutComponentTypesException.cs b/encompass-cs/Exceptions/DetectorWithoutComponentTypesException.cs new file mode 100644 index 0000000..f31d3af --- /dev/null +++ b/encompass-cs/Exceptions/DetectorWithoutComponentTypesException.cs @@ -0,0 +1,12 @@ +using System; + +namespace Encompass.Exceptions +{ + public class DetectorWithoutComponentTypesException : Exception + { + public DetectorWithoutComponentTypesException( + string format, + params object[] args + ) : base(string.Format(format, args)) { } + } +} diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index 13b8306..f349877 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -25,7 +25,7 @@ namespace Encompass this.renderManager = renderManager; } - public void Update(float dt) + public void Update(double dt) { foreach (var engine in enginesInOrder) { diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 0e5a1bf..abdfaeb 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Reflection; using System.Linq; +using Encompass.Exceptions; namespace Encompass { @@ -39,9 +40,9 @@ namespace Encompass { var engine = new TEngine(); - engine.AssignEntityManager(this.entityManager); - engine.AssignComponentManager(this.componentManager); - engine.AssignMessageManager(this.messageManager); + engine.AssignEntityManager(entityManager); + engine.AssignComponentManager(componentManager); + engine.AssignMessageManager(messageManager); engines.Add(engine); diff --git a/encompass-cs/attributes/Renders.cs b/encompass-cs/attributes/Renders.cs index 70a3c28..9c4e627 100644 --- a/encompass-cs/attributes/Renders.cs +++ b/encompass-cs/attributes/Renders.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace Encompass { diff --git a/encompass-cs/exceptions/EngineCycleException.cs b/encompass-cs/exceptions/EngineCycleException.cs index 2849787..de4571f 100644 --- a/encompass-cs/exceptions/EngineCycleException.cs +++ b/encompass-cs/exceptions/EngineCycleException.cs @@ -1,7 +1,7 @@ using System; -namespace Encompass +namespace Encompass.Exceptions { public class EngineCycleException : Exception { diff --git a/encompass-cs/exceptions/EngineMutationConflictException.cs b/encompass-cs/exceptions/EngineMutationConflictException.cs index d6cf4d7..22bd8ce 100644 --- a/encompass-cs/exceptions/EngineMutationConflictException.cs +++ b/encompass-cs/exceptions/EngineMutationConflictException.cs @@ -1,6 +1,6 @@ using System; -namespace Encompass +namespace Encompass.Exceptions { public class EngineMutationConflictException : Exception { diff --git a/encompass-cs/exceptions/IllegalComponentMutationException.cs b/encompass-cs/exceptions/IllegalComponentMutationException.cs index 7e2be40..dfcd7ae 100644 --- a/encompass-cs/exceptions/IllegalComponentMutationException.cs +++ b/encompass-cs/exceptions/IllegalComponentMutationException.cs @@ -1,6 +1,6 @@ using System; -namespace Encompass +namespace Encompass.Exceptions { public class IllegalComponentMutationException : Exception { diff --git a/encompass-cs/exceptions/IllegalMessageEmitException.cs b/encompass-cs/exceptions/IllegalMessageEmitException.cs index 79972f3..411c829 100644 --- a/encompass-cs/exceptions/IllegalMessageEmitException.cs +++ b/encompass-cs/exceptions/IllegalMessageEmitException.cs @@ -1,6 +1,6 @@ using System; -namespace Encompass +namespace Encompass.Exceptions { public class IllegalMessageEmitException : Exception { diff --git a/encompass-cs/exceptions/IllegalMessageReadException.cs b/encompass-cs/exceptions/IllegalMessageReadException.cs index 4a1c1f6..fe8fb32 100644 --- a/encompass-cs/exceptions/IllegalMessageReadException.cs +++ b/encompass-cs/exceptions/IllegalMessageReadException.cs @@ -1,6 +1,6 @@ using System; -namespace Encompass +namespace Encompass.Exceptions { public class IllegalMessageReadException : Exception { diff --git a/test/DetectorTest.cs b/test/DetectorTest.cs new file mode 100644 index 0000000..36753fc --- /dev/null +++ b/test/DetectorTest.cs @@ -0,0 +1,125 @@ +using NUnit.Framework; +using FluentAssertions; + +using Encompass; +using Encompass.Engines; +using Encompass.Exceptions; +using System; +using System.Collections.Generic; + +namespace Tests +{ + class DetectorTest + { + class NoComponentTypesDetector : Detector + { + public override void Detect(Entity entity, double dt) { } + } + + [Test] + public void DetectorWithNoComponentTypes() + { + var worldBuilder = new WorldBuilder(); + + Action addEngine = () => worldBuilder.AddEngine(); + addEngine.Should() + .Throw() + .WithInnerException(); + } + + struct AComponent : IComponent { } + struct BComponent : IComponent { } + struct CComponent : IComponent { } + + static List trackedEntities = new List(); + + [Detects(typeof(AComponent), typeof(BComponent))] + class TestDetector : Detector + { + public override void Detect(Entity entity, double dt) + { + trackedEntities.Add(entity); + } + } + + [Test] + public void CheckAndTrackEntities() + { + var worldBuilder = new WorldBuilder(); + var detector = worldBuilder.AddEngine(); + + var entityToTrack = worldBuilder.CreateEntity(); + entityToTrack.AddComponent(new AComponent()); + entityToTrack.AddComponent(new BComponent()); + + var entityNotToTrack = worldBuilder.CreateEntity(); + entityNotToTrack.AddComponent(new AComponent()); + entityNotToTrack.AddComponent(new CComponent()); + + var entityWithDeactivatedComponents = worldBuilder.CreateEntity(); + var aComponent = entityWithDeactivatedComponents.AddComponent(new AComponent()); + entityWithDeactivatedComponents.AddComponent(new BComponent()); + entityWithDeactivatedComponents.DeactivateComponent(aComponent); + + var entityWithOneDeactivatedComponent = worldBuilder.CreateEntity(); + var inactiveComponent = entityWithOneDeactivatedComponent.AddComponent(new AComponent()); + entityWithOneDeactivatedComponent.AddComponent(new AComponent()); + entityWithOneDeactivatedComponent.AddComponent(new BComponent()); + entityWithOneDeactivatedComponent.DeactivateComponent(inactiveComponent); + + var world = worldBuilder.Build(); + world.Update(0.01); + + trackedEntities.Should().Contain(entityToTrack); + trackedEntities.Should().NotContain(entityNotToTrack); + trackedEntities.Should().NotContain(entityWithDeactivatedComponents); + trackedEntities.Should().Contain(entityWithOneDeactivatedComponent); + } + + [Test] + public void EntityUntrackedWhenComponentRemoved() + { + var worldBuilder = new WorldBuilder(); + worldBuilder.AddEngine(); + + var entityToUntrack = worldBuilder.CreateEntity(); + entityToUntrack.AddComponent(new AComponent()); + var bComponent = entityToUntrack.AddComponent(new BComponent()); + + var world = worldBuilder.Build(); + + // have to update twice because we are updating from outside the world + entityToUntrack.RemoveComponent(bComponent); + world.Update(0.01); + + trackedEntities.Clear(); + world.Update(0.01); + + trackedEntities.Should().NotContain(entityToUntrack); + } + + [Test] + public void DetectCalledPerTrackedEntityOnWorldUpdat() + { + var worldBuilder = new WorldBuilder(); + worldBuilder.AddEngine(); + + var entityOne = worldBuilder.CreateEntity(); + entityOne.AddComponent(new AComponent()); + entityOne.AddComponent(new BComponent()); + + var entityTwo = worldBuilder.CreateEntity(); + entityTwo.AddComponent(new AComponent()); + entityTwo.AddComponent(new BComponent()); + + trackedEntities.Clear(); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + trackedEntities.Should().Contain(entityOne); + trackedEntities.Should().Contain(entityTwo); + } + } +} diff --git a/test/EngineTest.cs b/test/EngineTest.cs index 069c49f..1c1a364 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -6,6 +6,7 @@ using Encompass; using System; using System.Linq; using System.Collections.Generic; +using Encompass.Exceptions; namespace Tests { @@ -18,7 +19,7 @@ namespace Tests public class ReadComponentsTestEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { resultComponents = this.ReadComponents().ToList(); } @@ -26,7 +27,7 @@ namespace Tests public class ReadComponentTestEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { resultComponent = this.ReadComponent().Value; } @@ -108,7 +109,7 @@ namespace Tests [Mutates(typeof(MockComponent))] public class UpdateComponentTestEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { (var componentID, var component) = this.ReadComponent(); @@ -144,7 +145,7 @@ namespace Tests public class UndeclaredUpdateComponentTestEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { (var componentID, var component) = this.ReadComponent(); @@ -184,7 +185,7 @@ namespace Tests [Emits(typeof(MockMessage))] public class MessageEmitEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { MockMessage message; message.myString = "howdy"; @@ -196,7 +197,7 @@ namespace Tests [Reads(typeof(MockMessage))] public class MessageReadEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { resultMessages = this.ReadMessages().ToList(); } @@ -218,7 +219,7 @@ namespace Tests public class UndeclaredMessageEmitEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { MockMessage message; message.myString = "howdy"; @@ -244,7 +245,7 @@ namespace Tests [Emits(typeof(MockMessage))] class EmitMockMessageEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { MockMessage message; message.myString = "howdy"; @@ -256,7 +257,7 @@ namespace Tests [Reads(typeof(MockMessage))] class SomeTestEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { someTest = this.Some(); } @@ -278,7 +279,7 @@ namespace Tests class UndeclaredSomeEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { someTest = this.Some(); } @@ -301,9 +302,9 @@ namespace Tests class SameValueComponentReadEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { - var components = this.ReadComponents(); + var components = ReadComponents(); pairA = components.First(); pairB = components.Last(); @@ -340,9 +341,9 @@ namespace Tests class ReadEmptyMockComponentsEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { - emptyComponentReadResult = this.ReadComponents(); + emptyComponentReadResult = ReadComponents(); } } @@ -362,7 +363,7 @@ namespace Tests class DestroyerEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { var componentPairs = ReadComponents(); @@ -378,7 +379,7 @@ namespace Tests static IEnumerable> results; class ReaderEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { results = ReadComponents(); } diff --git a/test/WorldBuilderTest.cs b/test/WorldBuilderTest.cs index 29093eb..6cfdefa 100644 --- a/test/WorldBuilderTest.cs +++ b/test/WorldBuilderTest.cs @@ -2,6 +2,7 @@ using NUnit.Framework; using Encompass; using System.Collections.Generic; +using Encompass.Exceptions; namespace Tests { @@ -16,7 +17,7 @@ namespace Tests [Emits(typeof(BMessage))] class AEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { BMessage message; this.EmitMessage(message); @@ -27,7 +28,7 @@ namespace Tests [Emits(typeof(AMessage))] class BEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { AMessage message; this.EmitMessage(message); @@ -56,7 +57,7 @@ namespace Tests [Emits(typeof(BMessage))] class AEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { BMessage message; this.EmitMessage(message); @@ -67,7 +68,7 @@ namespace Tests [Emits(typeof(CMessage))] class BEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { CMessage message; this.EmitMessage(message); @@ -78,7 +79,7 @@ namespace Tests [Emits(typeof(DMessage))] class CEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { DMessage message; this.EmitMessage(message); @@ -89,7 +90,7 @@ namespace Tests [Emits(typeof(AMessage))] class DEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { AMessage message; this.EmitMessage(message); @@ -116,13 +117,13 @@ namespace Tests [Mutates(typeof(AComponent))] class AEngine : Engine { - public override void Update(float dt) { } + public override void Update(double dt) { } } [Mutates(typeof(AComponent))] class BEngine : Engine { - public override void Update(float dt) { } + public override void Update(double dt) { } } [Test] @@ -152,7 +153,7 @@ namespace Tests [Emits(typeof(AMessage))] class AEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { order.Add(this); } @@ -162,7 +163,7 @@ namespace Tests [Emits(typeof(BMessage))] class BEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { order.Add(this); } @@ -172,7 +173,7 @@ namespace Tests [Emits(typeof(DMessage))] class CEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { order.Add(this); } @@ -181,7 +182,7 @@ namespace Tests [Reads(typeof(DMessage))] class DEngine : Engine { - public override void Update(float dt) + public override void Update(double dt) { order.Add(this); }