rework EntityRenderer to OrderedRenderer

pull/5/head
Evan Hemsley 2019-08-01 15:06:19 -07:00
parent 2fd054d8e0
commit ec3b0df585
15 changed files with 116 additions and 385 deletions

View File

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

View File

@ -22,19 +22,9 @@ namespace Encompass
private readonly Dictionary<Guid, IComponent> pendingUpdates = new Dictionary<Guid, IComponent>();
//shared references with EntityManager
private readonly HashSet<Guid> entitiesWithAddedComponents;
private readonly HashSet<Guid> entitiesWithRemovedComponents;
public ComponentManager(
DrawLayerManager drawLayerManager,
HashSet<Guid> entitiesWithAddedComponents,
HashSet<Guid> entitiesWithRemovedComponents
)
public ComponentManager(DrawLayerManager drawLayerManager)
{
this.drawLayerManager = drawLayerManager;
this.entitiesWithAddedComponents = entitiesWithAddedComponents;
this.entitiesWithRemovedComponents = entitiesWithRemovedComponents;
}
internal void RegisterEntity(Guid entityID)
@ -68,8 +58,6 @@ namespace Encompass
componentIDToEntityID[componentID] = entity.ID;
entitiesWithAddedComponents.Add(entity.ID);
return componentID;
}
@ -210,8 +198,6 @@ namespace Encompass
typeToComponentIDs[type].Remove(componentID);
drawLayerManager.UnRegisterComponentWithLayer(componentID);
entitiesWithRemovedComponents.Add(entityID);
}
public void RegisterDestroyedEntity(Guid entityID)

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Encompass
{
class ComponentMessageManager
{
private readonly Dictionary<Type, HashSet<IMessage>> componentMessageTypeToComponentMessages = new Dictionary<Type, HashSet<IMessage>>();
}
}

View File

@ -9,23 +9,11 @@ namespace Encompass
private readonly HashSet<Guid> entitiesMarkedForDestroy = new HashSet<Guid>();
private readonly Dictionary<Type, HashSet<IEntityTracker>> componentTypeToEntityTrackers = new Dictionary<Type, HashSet<IEntityTracker>>();
private readonly Dictionary<Guid, HashSet<IEntityTracker>> entityToEntityTrackers = new Dictionary<Guid, HashSet<IEntityTracker>>();
private readonly HashSet<Guid> entitiesWithAddedComponents;
private readonly HashSet<Guid> entitiesWithRemovedComponents;
private readonly ComponentManager componentManager;
public EntityManager(
ComponentManager componentManager,
HashSet<Guid> entitiesWithAddedComponents,
HashSet<Guid> entitiesWithRemovedComponents
)
public EntityManager(ComponentManager componentManager)
{
this.componentManager = componentManager;
this.entitiesWithAddedComponents = entitiesWithAddedComponents;
this.entitiesWithRemovedComponents = entitiesWithRemovedComponents;
}
public Entity CreateEntity()
@ -58,7 +46,6 @@ namespace Encompass
{
componentManager.MarkAllComponentsOnEntityForRemoval(entityID);
IDToEntity.Remove(entityID);
entityToEntityTrackers.Remove(entityID);
componentManager.RegisterDestroyedEntity(entityID);
}
@ -69,87 +56,5 @@ namespace Encompass
{
return Guid.NewGuid();
}
public void RegisterEntityTracker(IEntityTracker entityTracker)
{
foreach (var componentType in entityTracker.ComponentTypes)
{
if (!componentTypeToEntityTrackers.ContainsKey(componentType))
{
componentTypeToEntityTrackers.Add(componentType, new HashSet<IEntityTracker>());
}
componentTypeToEntityTrackers[componentType].Add(entityTracker);
}
if (entityTracker is EntityRenderer)
{
var entityRenderer = entityTracker as EntityRenderer;
if (!componentTypeToEntityTrackers.ContainsKey(entityRenderer.DrawComponentType))
{
componentTypeToEntityTrackers.Add(entityRenderer.DrawComponentType, new HashSet<IEntityTracker>());
}
componentTypeToEntityTrackers[entityRenderer.DrawComponentType].Add(entityRenderer);
}
}
public void RegisterDirtyEntityWithAddedComponents(Guid entityID)
{
entitiesWithAddedComponents.Add(entityID);
}
public void RegisterDirtyEntityWithRemovedComponents(Guid entityID)
{
entitiesWithRemovedComponents.Add(entityID);
}
public void CheckEntitiesWithAddedComponents()
{
foreach (var entityID in entitiesWithAddedComponents)
{
CheckAndRegisterEntity(entityID);
}
entitiesWithAddedComponents.Clear();
}
public void CheckEntitiesWithRemovedComponents()
{
foreach (var entityID in entitiesWithRemovedComponents)
{
if (entityToEntityTrackers.ContainsKey(entityID))
{
foreach (var engine in entityToEntityTrackers[entityID])
{
engine.CheckAndUntrackEntity(entityID);
}
}
}
entitiesWithRemovedComponents.Clear();
}
private void CheckAndRegisterEntity(Guid entityID)
{
var componentTypes = componentManager.GetAllComponentTypesOfEntity(entityID);
foreach (var componentType in componentTypes)
{
if (componentTypeToEntityTrackers.ContainsKey(componentType))
{
foreach (var entityTracker in componentTypeToEntityTrackers[componentType])
{
if (entityTracker.CheckAndTrackEntity(entityID))
{
if (!entityToEntityTrackers.ContainsKey(entityID))
{
entityToEntityTrackers.Add(entityID, new HashSet<IEntityTracker>());
}
entityToEntityTrackers[entityID].Add(entityTracker);
}
}
}
}
}
}
}

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
namespace Encompass
{
internal interface IEntityTracker
{
IEnumerable<Type> ComponentTypes { get; }
bool CheckAndTrackEntity(Guid entityID);
bool CheckAndUntrackEntity(Guid entityID);
}
}

View File

@ -10,7 +10,7 @@ namespace Encompass
private readonly ComponentManager componentManager;
private readonly DrawLayerManager drawLayerManager;
private readonly Dictionary<Type, EntityRenderer> drawComponentTypeToEntityRenderer = new Dictionary<Type, EntityRenderer>();
private readonly Dictionary<Type, Action<Guid, IComponent>> drawComponentTypeToOrderedRenderer = new Dictionary<Type, Action<Guid, IComponent>>();
public RenderManager(
EntityManager entityManager,
@ -23,13 +23,9 @@ namespace Encompass
this.drawLayerManager = drawLayerManager;
}
public void RegisterEntityRenderer(EntityRenderer renderer)
public void RegisterOrderedRenderer<TComponent>(Action<Guid, IComponent> renderAction) where TComponent : struct, IComponent
{
var rendersAttribute = renderer.GetType().GetCustomAttribute<Renders>(false);
if (rendersAttribute != null)
{
drawComponentTypeToEntityRenderer.Add(rendersAttribute.drawComponentType, renderer);
}
drawComponentTypeToOrderedRenderer.Add(typeof(TComponent), renderAction);
}
public void RegisterGeneralRendererWithLayer(GeneralRenderer renderer, int layer)
@ -49,16 +45,10 @@ namespace Encompass
var component = componentManager.GetComponentByID(componentID);
var componentType = componentManager.GetComponentTypeByID(componentID);
if (drawComponentTypeToEntityRenderer.ContainsKey(componentType))
if (drawComponentTypeToOrderedRenderer.ContainsKey(componentType))
{
var renderer = drawComponentTypeToEntityRenderer[componentType];
var entityID = componentManager.GetEntityIDByComponentID(componentID);
if (renderer.IsTracking(entityID) && entityManager.EntityExists(entityID))
{
var entity = entityManager.GetEntity(entityID);
renderer.Render(entity);
}
var internalRenderAction = drawComponentTypeToOrderedRenderer[componentType];
internalRenderAction(componentID, component);
}
}

View File

@ -1,54 +0,0 @@
using System;
using System.Reflection;
using System.Collections.Generic;
namespace Encompass
{
public abstract class EntityRenderer : Renderer, IEntityTracker
{
private readonly List<Type> componentTypes = new List<Type>();
private readonly EntityTracker entityTracker = new EntityTracker();
public IEnumerable<Type> ComponentTypes { get { return componentTypes; } }
public Type DrawComponentType { get; }
public abstract void Render(Entity entity);
protected EntityRenderer()
{
var rendersAttribute = GetType().GetCustomAttribute<Renders>(false);
if (rendersAttribute != null)
{
componentTypes = rendersAttribute.componentTypes;
DrawComponentType = rendersAttribute.drawComponentType;
}
}
bool IEntityTracker.CheckAndTrackEntity(Guid entityID)
{
var entity = GetEntity(entityID);
var shouldTrack = CheckEntity(entity);
if (shouldTrack) { entityTracker.TrackEntity(entityID); }
return shouldTrack;
}
bool IEntityTracker.CheckAndUntrackEntity(Guid entityID)
{
var entity = GetEntity(entityID);
var shouldUntrack = !CheckEntity(entity);
if (shouldUntrack) { entityTracker.UntrackEntity(entityID); }
return shouldUntrack;
}
public bool IsTracking(Guid entityID)
{
return entityTracker.IsTracking(entityID);
}
private bool CheckEntity(Entity entity)
{
return EntityChecker.CheckEntity(componentManager, entity, componentTypes)
&& componentManager.EntityHasComponentOfType(entity.ID, DrawComponentType);
}
}
}

View File

@ -0,0 +1,14 @@
using System;
namespace Encompass
{
public abstract class OrderedRenderer<TComponent> : Renderer where TComponent : struct, IComponent
{
public abstract void Render(Guid drawComponentID, TComponent drawComponent);
internal void InternalRender(Guid drawComponentId, IComponent component)
{
Render(drawComponentId, (TComponent)component);
}
}
}

View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Encompass
{
internal static class EntityChecker
{
public static bool CheckEntity(ComponentManager componentManager, Entity entity, IEnumerable<Type> componentTypes)
{
return componentTypes.All((componentType) => componentManager.EntityHasComponentOfType(entity.ID, componentType));
}
}
}

View File

@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Encompass
{
internal class EntityTracker
{
private readonly HashSet<Guid> trackedEntityIDs = new HashSet<Guid>();
public IEnumerable<Guid> TrackedEntityIDs
{
get { return trackedEntityIDs; }
}
public void TrackEntity(Guid entityID)
{
trackedEntityIDs.Add(entityID);
}
public void UntrackEntity(Guid entityID)
{
trackedEntityIDs.Remove(entityID);
}
public bool IsTracking(Guid entityID)
{
return trackedEntityIDs.Contains(entityID);
}
}
}

View File

@ -37,9 +37,6 @@ namespace Encompass
componentManager.PerformComponentUpdates();
componentManager.RemoveMarkedComponents();
entityManager.CheckEntitiesWithAddedComponents();
entityManager.CheckEntitiesWithRemovedComponents();
}
public void Draw()

View File

@ -30,8 +30,8 @@ namespace Encompass
var entitiesWithAddedComponents = new HashSet<Guid>();
var entitiesWithRemovedComponents = new HashSet<Guid>();
drawLayerManager = new DrawLayerManager();
componentManager = new ComponentManager(drawLayerManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);
entityManager = new EntityManager(componentManager, entitiesWithAddedComponents, entitiesWithRemovedComponents);
componentManager = new ComponentManager(drawLayerManager);
entityManager = new EntityManager(componentManager);
messageManager = new MessageManager();
renderManager = new RenderManager(entityManager, componentManager, drawLayerManager);
}
@ -141,17 +141,11 @@ namespace Encompass
return engine;
}
public TRenderer AddEntityRenderer<TRenderer>(TRenderer renderer) where TRenderer : Renderer
public OrderedRenderer<TComponent> AddOrderedRenderer<TComponent>(OrderedRenderer<TComponent> renderer) where TComponent : struct, IComponent
{
renderer.AssignEntityManager(entityManager);
renderer.AssignComponentManager(componentManager);
if (renderer is EntityRenderer)
{
entityManager.RegisterEntityTracker(renderer as IEntityTracker);
renderManager.RegisterEntityRenderer(renderer as EntityRenderer);
}
renderManager.RegisterOrderedRenderer<TComponent>(renderer.InternalRender);
return renderer;
}
@ -262,9 +256,6 @@ namespace Encompass
componentManager.PerformComponentUpdates();
componentManager.RemoveMarkedComponents();
entityManager.CheckEntitiesWithAddedComponents();
entityManager.CheckEntitiesWithRemovedComponents();
return world;
}
}

View File

@ -1,102 +0,0 @@
using System;
using NUnit.Framework;
using FluentAssertions;
using Encompass;
using System.Collections.Generic;
namespace Tests
{
public class EntityRendererTest
{
struct AComponent : IComponent { }
struct BComponent : IComponent { }
struct CComponent : IComponent { }
struct TestDrawComponent : IComponent { }
[Renders(typeof(TestDrawComponent), typeof(AComponent), typeof(BComponent))]
class TestRenderer : EntityRenderer
{
public override void Render(Entity entity) { }
}
[Test]
public void CheckAndTrackEntities()
{
var worldBuilder = new WorldBuilder();
var renderer = worldBuilder.AddEntityRenderer(new TestRenderer());
AComponent aComponent;
BComponent bComponent;
TestDrawComponent testDrawComponent = default(TestDrawComponent);
var entityToTrack = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entityToTrack, aComponent);
worldBuilder.AddComponent(entityToTrack, bComponent);
worldBuilder.AddComponent(entityToTrack, testDrawComponent);
var entityNotToTrack = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entityNotToTrack, aComponent);
worldBuilder.AddComponent(entityNotToTrack, testDrawComponent);
var entityWithoutDrawComponent = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entityWithoutDrawComponent, aComponent);
worldBuilder.AddComponent(entityWithoutDrawComponent, bComponent);
var world = worldBuilder.Build();
world.Update(0.01f);
Assert.IsTrue(renderer.IsTracking(entityToTrack.ID));
Assert.IsFalse(renderer.IsTracking(entityNotToTrack.ID));
Assert.IsFalse(renderer.IsTracking(entityWithoutDrawComponent.ID));
}
static bool called = false;
class DeactivatedRenderer : TestRenderer
{
public override void Render(Entity entity)
{
called = true;
}
}
static bool calledOnDraw = false;
static IEnumerable<ValueTuple<Guid, TestDrawComponent>> resultComponents;
[Renders(typeof(TestDrawComponent), typeof(AComponent), typeof(CComponent))]
class CalledRenderer : EntityRenderer
{
public override void Render(Entity entity)
{
resultComponents = GetComponents<TestDrawComponent>(entity);
calledOnDraw = true;
}
}
[Test]
public void RenderMethodCalledOnWorldDraw()
{
var worldBuilder = new WorldBuilder();
var renderer = worldBuilder.AddEntityRenderer(new CalledRenderer());
AComponent aComponent;
CComponent cComponent;
TestDrawComponent testDrawComponent;
var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, aComponent);
worldBuilder.AddComponent(entity, cComponent);
var testDrawComponentID = worldBuilder.AddDrawComponent(entity, testDrawComponent, 2);
var world = worldBuilder.Build();
world.Update(0.01f);
world.Draw();
Assert.IsTrue(renderer.IsTracking(entity.ID));
Assert.IsTrue(calledOnDraw);
resultComponents.Should().Contain(new ValueTuple<Guid, TestDrawComponent>(testDrawComponentID, testDrawComponent));
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using NUnit.Framework;
using FluentAssertions;
using Encompass;
using System.Collections.Generic;
namespace Tests
{
public class OrderedRendererTest
{
struct AComponent : IComponent { }
struct BComponent : IComponent { }
struct CComponent : IComponent { }
struct TestDrawComponent : IComponent { }
class TestRenderer : OrderedRenderer<TestDrawComponent>
{
public override void Render(Guid drawComponentID, TestDrawComponent testDrawComponent) { }
}
static bool called = false;
class DeactivatedRenderer : TestRenderer
{
public override void Render(Guid drawComponentID, TestDrawComponent testDrawComponent)
{
called = true;
}
}
static bool calledOnDraw = false;
static ValueTuple<Guid, TestDrawComponent> resultComponent;
class CalledRenderer : OrderedRenderer<TestDrawComponent>
{
public override void Render(Guid drawComponentID, TestDrawComponent testDrawComponent)
{
resultComponent = (drawComponentID, testDrawComponent);
calledOnDraw = true;
}
}
[Test]
public void RenderMethodCalledOnWorldDraw()
{
var worldBuilder = new WorldBuilder();
var renderer = worldBuilder.AddOrderedRenderer(new CalledRenderer());
AComponent aComponent;
CComponent cComponent;
TestDrawComponent testDrawComponent;
var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, aComponent);
worldBuilder.AddComponent(entity, cComponent);
var testDrawComponentID = worldBuilder.AddDrawComponent(entity, testDrawComponent, 2);
var world = worldBuilder.Build();
world.Update(0.01f);
world.Draw();
Assert.IsTrue(calledOnDraw);
resultComponent.Should().BeEquivalentTo((testDrawComponentID, testDrawComponent));
}
}
}

View File

@ -16,12 +16,11 @@ namespace Tests
static List<object> drawOrder = new List<object>();
[Renders(typeof(TestDrawComponent), typeof(TestComponent))]
class TestEntityRenderer : EntityRenderer
class TestEntityRenderer : OrderedRenderer<TestDrawComponent>
{
public override void Render(Entity entity)
public override void Render(Guid drawComponentID, TestDrawComponent testDrawComponent)
{
drawOrder.Add(entity);
drawOrder.Add(drawComponentID);
}
}
@ -37,7 +36,7 @@ namespace Tests
public void DrawOrder()
{
var worldBuilder = new WorldBuilder();
worldBuilder.AddEntityRenderer(new TestEntityRenderer());
worldBuilder.AddOrderedRenderer(new TestEntityRenderer());
var testGeneralRenderer = worldBuilder.AddGeneralRenderer(new TestGeneralRenderer(), 7);
TestComponent testComponent;
@ -45,32 +44,32 @@ namespace Tests
var entity = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entity, testComponent);
worldBuilder.AddDrawComponent(entity, testDrawComponent, 3);
var testDrawComponentOneID = worldBuilder.AddDrawComponent(entity, testDrawComponent, 3);
TestDrawComponent testDrawComponentTwo = default(TestDrawComponent);
var entityTwo = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entityTwo, testComponent);
worldBuilder.AddDrawComponent(entityTwo, testDrawComponentTwo, 1);
var testDrawComponentTwoID = worldBuilder.AddDrawComponent(entityTwo, testDrawComponentTwo, 1);
TestDrawComponent testDrawComponentThree = default(TestDrawComponent);
var entityThree = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entityThree, testComponent);
worldBuilder.AddDrawComponent(entityThree, testDrawComponentThree, 5);
var testDrawComponentThreeID = worldBuilder.AddDrawComponent(entityThree, testDrawComponentThree, 5);
TestDrawComponent testDrawComponentFour = default(TestDrawComponent);
var entityFour = worldBuilder.CreateEntity();
worldBuilder.AddComponent(entityFour, testComponent);
worldBuilder.AddDrawComponent(entityFour, testDrawComponentFour, -5);
var testDrawComponentFourID = worldBuilder.AddDrawComponent(entityFour, testDrawComponentFour, -5);
var world = worldBuilder.Build();
world.Update(0.01f);
world.Draw();
drawOrder.Should().BeEquivalentTo(entityFour, entityTwo, entity, entityThree, testGeneralRenderer);
drawOrder.Should().BeEquivalentTo(testDrawComponentFourID, testDrawComponentTwoID, testDrawComponentOneID, testDrawComponentThreeID, testGeneralRenderer);
}
}
}