initial rendering implementation

pull/5/head
Evan Hemsley 2019-06-19 14:14:44 -07:00
parent af401398be
commit 5941029927
16 changed files with 465 additions and 9 deletions

View File

@ -6,6 +6,7 @@ namespace Encompass
{ {
internal class ComponentManager internal class ComponentManager
{ {
private Dictionary<Guid, Type> componentIDToType = new Dictionary<Guid, Type>();
private Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>(); private Dictionary<Guid, IComponent> IDToComponent = new Dictionary<Guid, IComponent>();
private Dictionary<Guid, List<Guid>> entityIDToComponentIDs = new Dictionary<Guid, List<Guid>>(); private Dictionary<Guid, List<Guid>> entityIDToComponentIDs = new Dictionary<Guid, List<Guid>>();
private Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>(); private Dictionary<Guid, Guid> componentIDToEntityID = new Dictionary<Guid, Guid>();
@ -22,6 +23,7 @@ namespace Encompass
var componentID = Guid.NewGuid(); var componentID = Guid.NewGuid();
IDToComponent[componentID] = component; IDToComponent[componentID] = component;
componentIDToType[componentID] = typeof(TComponent);
if (!entityIDToComponentIDs.ContainsKey(entityID)) if (!entityIDToComponentIDs.ContainsKey(entityID))
{ {
@ -54,6 +56,13 @@ namespace Encompass
Enumerable.Empty<KeyValuePair<Guid, TComponent>>(); Enumerable.Empty<KeyValuePair<Guid, TComponent>>();
} }
internal IEnumerable<KeyValuePair<Guid, IComponent>> GetActiveComponentsByType(Type type)
{
return activeComponents.ContainsKey(type) ?
activeComponents[type].Select((id) => new KeyValuePair<Guid, IComponent>(id, IDToComponent[id])) :
Enumerable.Empty<KeyValuePair<Guid, IComponent>>();
}
internal KeyValuePair<Guid, TComponent> GetActiveComponentByType<TComponent>() where TComponent : struct, IComponent internal KeyValuePair<Guid, TComponent> GetActiveComponentByType<TComponent>() where TComponent : struct, IComponent
{ {
return GetActiveComponentsByType<TComponent>().Single(); return GetActiveComponentsByType<TComponent>().Single();
@ -66,14 +75,41 @@ namespace Encompass
return entity_components.Intersect(active_components_by_type); return entity_components.Intersect(active_components_by_type);
} }
internal IEnumerable<KeyValuePair<Guid, IComponent>> GetComponentsByEntityAndType(Guid entityID, Type type)
{
var entityComponents = GetComponentsByEntity(entityID);
var activeComponentsByType = GetActiveComponentsByType(type);
return entityComponents.Intersect(activeComponentsByType);
}
internal bool EntityHasComponentOfType<TComponent>(Guid entityID) where TComponent : struct, IComponent internal bool EntityHasComponentOfType<TComponent>(Guid entityID) where TComponent : struct, IComponent
{ {
return GetComponentsByEntityAndType<TComponent>(entityID).Any(); return GetComponentsByEntityAndType<TComponent>(entityID).Any();
} }
internal bool EntityHasComponentOfType(Guid entityID, Type type)
{
return GetComponentsByEntityAndType(entityID, type).Any();
}
internal IComponent GetComponentByID(Guid componentID)
{
return IDToComponent[componentID];
}
internal Type GetComponentTypeByID(Guid componentID)
{
return componentIDToType[componentID];
}
internal Guid GetEntityIDFromComponentID(Guid componentID)
{
return componentIDToEntityID[componentID];
}
internal void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent internal void UpdateComponent<TComponent>(Guid componentID, TComponent newComponentValue) where TComponent : struct, IComponent
{ {
var entityID = componentIDToEntityID[componentID]; var entityID = GetEntityIDFromComponentID(componentID);
IDToComponent[componentID] = newComponentValue; IDToComponent[componentID] = newComponentValue;
} }

View File

@ -36,6 +36,11 @@ namespace Encompass
return componentManager.EntityHasComponentOfType<TComponent>(id); return componentManager.EntityHasComponentOfType<TComponent>(id);
} }
internal bool HasComponent(Type type)
{
return componentManager.EntityHasComponentOfType(id, type);
}
internal void RemoveAllComponents() internal void RemoveAllComponents()
{ {
componentManager.RemoveAllComponentsFromEntity(id); componentManager.RemoveAllComponentsFromEntity(id);

View File

@ -26,7 +26,7 @@ namespace Encompass
public Entity GetEntity(Guid id) public Entity GetEntity(Guid id)
{ {
return this.IDToEntity[id]; return IDToEntity[id];
} }
public void MarkForDestroy(Entity entity) public void MarkForDestroy(Entity entity)

View File

@ -0,0 +1,6 @@
namespace Encompass
{
public interface IDrawComponent : IComponent, IRenderable
{
}
}

View File

@ -0,0 +1,7 @@
namespace Encompass
{
public interface IRenderable
{
int Layer { get; set; }
}
}

View File

@ -0,0 +1,134 @@
using System;
using System.Reflection;
using System.Collections.Generic;
namespace Encompass
{
internal class RenderManager
{
private EntityManager entityManager;
private ComponentManager componentManager;
private SortedList<int, int> layerOrder = new SortedList<int, int>();
private Dictionary<int, HashSet<Guid>> layerIndexToComponentIDs = new Dictionary<int, HashSet<Guid>>();
private Dictionary<int, HashSet<GeneralRenderer>> layerIndexToGeneralRenderers = new Dictionary<int, HashSet<GeneralRenderer>>();
private Dictionary<Type, EntityRenderer> drawComponentTypeToEntityRenderer = new Dictionary<Type, EntityRenderer>();
public RenderManager(EntityManager entityManager, ComponentManager componentManager)
{
this.entityManager = entityManager;
this.componentManager = componentManager;
}
public void RegisterEntityRenderer(EntityRenderer renderer)
{
var rendersAttribute = renderer.GetType().GetCustomAttribute<Renders>(false);
if (rendersAttribute != null)
{
drawComponentTypeToEntityRenderer.Add(rendersAttribute.drawComponentType, renderer);
}
}
public void RegisterGeneralRendererWithLayer(GeneralRenderer renderer, int layer)
{
if (layerIndexToGeneralRenderers.ContainsKey(layer))
{
var set = layerIndexToGeneralRenderers[layer];
set.Add(renderer);
}
else
{
var set = new HashSet<GeneralRenderer>();
layerIndexToGeneralRenderers.Add(layer, set);
set.Add(renderer);
}
if (!layerOrder.ContainsKey(layer))
{
layerOrder.Add(layer, layer);
}
}
public void UnregisterGeneralRendererWithLayer(GeneralRenderer renderer, int layer)
{
if (layerIndexToGeneralRenderers.ContainsKey(layer))
{
layerIndexToGeneralRenderers[layer].Remove(renderer);
}
}
public void AdjustRendererLayer(GeneralRenderer renderer, int oldLayer, int newLayer)
{
UnregisterGeneralRendererWithLayer(renderer, oldLayer);
RegisterGeneralRendererWithLayer(renderer, newLayer);
}
public void RegisterComponentWithLayer(Guid id, int layer)
{
if (layerIndexToComponentIDs.ContainsKey(layer))
{
var set = layerIndexToComponentIDs[layer];
set.Add(id);
}
else
{
var set = new HashSet<Guid>();
layerIndexToComponentIDs.Add(layer, set);
set.Add(id);
}
if (!layerOrder.ContainsKey(layer))
{
layerOrder.Add(layer, layer);
}
}
public void UnRegisterComponentWithLayer(Guid id, int layer)
{
if (layerIndexToComponentIDs.ContainsKey(layer))
{
layerIndexToComponentIDs[layer].Remove(id);
}
}
public void AdjustComponentLayer(Guid id, int oldLayer, int newLayer)
{
UnRegisterComponentWithLayer(id, oldLayer);
RegisterComponentWithLayer(id, newLayer);
}
public void Draw()
{
foreach (var layerKVPair in layerOrder)
{
var layer = layerKVPair.Key;
var componentIDSet = layerIndexToComponentIDs[layer];
var generalRendererSet = layerIndexToGeneralRenderers[layer];
foreach (var componentID in componentIDSet)
{
var component = componentManager.GetComponentByID(componentID);
var componentType = componentManager.GetComponentTypeByID(componentID);
if (drawComponentTypeToEntityRenderer.ContainsKey(componentType))
{
var renderer = drawComponentTypeToEntityRenderer[componentType];
var entityID = componentManager.GetEntityIDFromComponentID(componentID);
if (renderer.IsTracking(entityID))
{
var entity = entityManager.GetEntity(entityID);
renderer.Render(entity);
}
}
}
foreach (var generalRenderer in generalRendererSet)
{
generalRenderer.Render();
}
}
}
}
}

37
encompass-cs/Renderer.cs Normal file
View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Encompass
{
public abstract class Renderer
{
private EntityManager entityManager;
private ComponentManager componentManager;
internal void AssignEntityManager(EntityManager entityManager)
{
this.entityManager = entityManager;
}
internal void AssignComponentManager(ComponentManager componentManager)
{
this.componentManager = componentManager;
}
protected Entity GetEntity(Guid entityID)
{
return entityManager.GetEntity(entityID);
}
protected IEnumerable<KeyValuePair<Guid, TComponent>> ReadComponents<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetActiveComponentsByType<TComponent>();
}
protected KeyValuePair<Guid, TComponent> ReadComponent<TComponent>() where TComponent : struct, IComponent
{
return componentManager.GetActiveComponentByType<TComponent>();
}
}
}

View File

@ -8,18 +8,21 @@ namespace Encompass
private EntityManager entityManager; private EntityManager entityManager;
private ComponentManager componentManager; private ComponentManager componentManager;
private MessageManager messageManager; private MessageManager messageManager;
private RenderManager renderManager;
internal World( internal World(
List<Engine> enginesInOrder, List<Engine> enginesInOrder,
EntityManager entityManager, EntityManager entityManager,
ComponentManager componentManager, ComponentManager componentManager,
MessageManager messageManager MessageManager messageManager,
RenderManager renderManager
) )
{ {
this.enginesInOrder = enginesInOrder; this.enginesInOrder = enginesInOrder;
this.entityManager = entityManager; this.entityManager = entityManager;
this.componentManager = componentManager; this.componentManager = componentManager;
this.messageManager = messageManager; this.messageManager = messageManager;
this.renderManager = renderManager;
} }
public void Update(float dt) public void Update(float dt)
@ -35,5 +38,10 @@ namespace Encompass
componentManager.DeactivateComponents(); componentManager.DeactivateComponents();
componentManager.RemoveComponents(); componentManager.RemoveComponents();
} }
public void Draw()
{
renderManager.Draw();
}
} }
} }

View File

@ -13,6 +13,7 @@ namespace Encompass
private ComponentManager componentManager; private ComponentManager componentManager;
private EntityManager entityManager; private EntityManager entityManager;
private MessageManager messageManager; private MessageManager messageManager;
private RenderManager renderManager;
private Dictionary<Type, HashSet<Engine>> messageTypeToEmitters = new Dictionary<Type, HashSet<Engine>>(); private Dictionary<Type, HashSet<Engine>> messageTypeToEmitters = new Dictionary<Type, HashSet<Engine>>();
private Dictionary<Type, HashSet<Engine>> messageTypeToReaders = new Dictionary<Type, HashSet<Engine>>(); private Dictionary<Type, HashSet<Engine>> messageTypeToReaders = new Dictionary<Type, HashSet<Engine>>();
@ -22,11 +23,12 @@ namespace Encompass
componentManager = new ComponentManager(); componentManager = new ComponentManager();
entityManager = new EntityManager(componentManager); entityManager = new EntityManager(componentManager);
messageManager = new MessageManager(); messageManager = new MessageManager();
renderManager = new RenderManager(entityManager, componentManager);
} }
public Entity CreateEntity() public Entity CreateEntity()
{ {
return this.entityManager.CreateEntity(); return entityManager.CreateEntity();
} }
public Engine AddEngine<TEngine>() where TEngine : Engine, new() public Engine AddEngine<TEngine>() where TEngine : Engine, new()
@ -88,6 +90,25 @@ namespace Encompass
return engine; return engine;
} }
public TRenderer AddRenderer<TRenderer>() where TRenderer : Renderer, new()
{
var renderer = new TRenderer();
renderer.AssignEntityManager(entityManager);
renderer.AssignComponentManager(componentManager);
if (renderer is EntityRenderer)
{
renderManager.RegisterEntityRenderer(renderer as EntityRenderer);
}
else if (renderer is GeneralRenderer)
{
var generalRenderer = renderer as GeneralRenderer;
renderManager.RegisterGeneralRendererWithLayer(generalRenderer, generalRenderer.Layer);
}
return renderer;
}
public World Build() public World Build()
{ {
if (engineGraph.Cyclic()) if (engineGraph.Cyclic())
@ -155,9 +176,10 @@ namespace Encompass
var world = new World( var world = new World(
engineOrder, engineOrder,
this.entityManager, entityManager,
this.componentManager, componentManager,
this.messageManager messageManager,
renderManager
); );
this.componentManager.ActivateComponents(); this.componentManager.ActivateComponents();

View File

@ -3,8 +3,8 @@ using System.Collections.Generic;
namespace Encompass namespace Encompass
{ {
[System.AttributeUsage(System.AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class)]
public class Emits : System.Attribute public class Emits : Attribute
{ {
public readonly List<Type> emitMessageTypes; public readonly List<Type> emitMessageTypes;

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
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

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Encompass
{
public abstract class EntityRenderer : Renderer
{
private readonly List<Type> componentTypes = new List<Type>();
private readonly Type drawComponentType;
private EntityTracker entityTracker = new EntityTracker();
public abstract void Render(Entity entity);
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;
}
public bool IsTracking(Guid entityID)
{
return entityTracker.IsTracking(entityID);
}
internal bool CheckEntity(Entity entity)
{
return EntityChecker.CheckEntity(entity, componentTypes) &&
entity.HasComponent(drawComponentType);
}
}
}

View File

@ -0,0 +1,8 @@
namespace Encompass
{
public abstract class GeneralRenderer : Renderer, IRenderable
{
public int Layer { get; set; }
public abstract void Render();
}
}

View File

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

View File

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

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Encompass;
namespace Tests
{
public class EntityRendererTest
{
struct AComponent : IComponent { }
struct BComponent : IComponent { }
struct CComponent : IComponent { }
struct TestDrawComponent : IDrawComponent
{
public int Layer { get; set; }
}
[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.AddRenderer<TestRenderer>();
AComponent aComponent;
BComponent bComponent;
TestDrawComponent testDrawComponent = default(TestDrawComponent);
var entityToTrack = worldBuilder.CreateEntity();
entityToTrack.AddComponent(aComponent);
entityToTrack.AddComponent(bComponent);
entityToTrack.AddComponent(testDrawComponent);
var entityNotToTrack = worldBuilder.CreateEntity();
entityNotToTrack.AddComponent(aComponent);
entityNotToTrack.AddComponent(testDrawComponent);
var entityWithoutDrawComponent = worldBuilder.CreateEntity();
entityWithoutDrawComponent.AddComponent(aComponent);
entityWithoutDrawComponent.AddComponent(bComponent);
var world = worldBuilder.Build();
world.Update(0.01f);
Console.WriteLine(renderer.IsTracking(entityNotToTrack.id));
Assert.IsTrue(renderer.IsTracking(entityToTrack.id));
Assert.IsFalse(renderer.IsTracking(entityNotToTrack.id));
Assert.IsFalse(renderer.IsTracking(entityWithoutDrawComponent.id));
}
}
}