advanced query system

pull/5/head
Evan Hemsley 2019-12-22 01:15:58 -08:00
parent 728109bfc6
commit 50974c181a
10 changed files with 317 additions and 99 deletions

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace Encompass namespace Encompass
@ -6,7 +7,12 @@ namespace Encompass
internal class ComponentStore internal class ComponentStore
{ {
private Dictionary<Type, TypedComponentStore> Stores = new Dictionary<Type, TypedComponentStore>(512); private Dictionary<Type, TypedComponentStore> Stores = new Dictionary<Type, TypedComponentStore>(512);
private ComponentBitSet componentBitSet = new ComponentBitSet(); private ComponentBitSet componentBitSet;
public ComponentStore(Dictionary<Type, int> typeToIndex)
{
componentBitSet = new ComponentBitSet(typeToIndex);
}
public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable() public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable()
{ {
@ -22,7 +28,6 @@ namespace Encompass
{ {
var store = new TypedComponentStore<TComponent>(); var store = new TypedComponentStore<TComponent>();
Stores.Add(typeof(TComponent), store); Stores.Add(typeof(TComponent), store);
componentBitSet.RegisterType<TComponent>();
} }
} }
@ -47,9 +52,9 @@ namespace Encompass
return Stores.ContainsKey(type) && Stores[type].Has(entity); return Stores.ContainsKey(type) && Stores[type].Has(entity);
} }
public IEnumerable<Entity> EntitiesWithComponents(IEnumerable<Type> types) public BitArray EntityBitArray(Entity entity)
{ {
return componentBitSet.EntitiesWithComponents(types); return componentBitSet.EntityBitArray(entity);
} }
public TComponent Get<TComponent>(Entity entity) where TComponent : struct, IComponent public TComponent Get<TComponent>(Entity entity) where TComponent : struct, IComponent

View File

@ -8,21 +8,25 @@ namespace Encompass
{ {
BitArrayPool bitArrayPool = new BitArrayPool(32768); // todo: set entity cap BitArrayPool bitArrayPool = new BitArrayPool(32768); // todo: set entity cap
Dictionary<Entity, BitArray> entities = new Dictionary<Entity, BitArray>(); Dictionary<Entity, BitArray> entities = new Dictionary<Entity, BitArray>();
Dictionary<Type, int> typeToIndex = new Dictionary<Type, int>(); Dictionary<Type, int> TypeToIndex { get; set; }
BitArray queryArray; BitArray withQueryArray;
BitArray withoutQueryArray;
BitArray emptyArray;
public void RegisterType<TComponent>() where TComponent : struct, IComponent public ComponentBitSet(Dictionary<Type, int> typeToIndex)
{ {
typeToIndex.Add(typeof(TComponent), typeToIndex.Count); TypeToIndex = typeToIndex;
foreach (var kvp in entities)
{
kvp.Value.Length = typeToIndex.Count;
}
} }
public void FinishRegistering() public void FinishRegistering()
{ {
queryArray = new BitArray(typeToIndex.Count); withQueryArray = new BitArray(TypeToIndex.Count);
withoutQueryArray = new BitArray(TypeToIndex.Count);
emptyArray = new BitArray(TypeToIndex.Count);
foreach (var kvp in entities)
{
kvp.Value.Length = TypeToIndex.Count;
}
} }
public void Clear() public void Clear()
@ -36,19 +40,19 @@ namespace Encompass
public void AddEntity(Entity entity) public void AddEntity(Entity entity)
{ {
var bitArray = bitArrayPool.Obtain(typeToIndex.Count); var bitArray = bitArrayPool.Obtain(TypeToIndex.Count);
entities.Add(entity, bitArray); entities.Add(entity, bitArray);
} }
public void Set<TComponent>(Entity entity) where TComponent : struct, IComponent public void Set<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
if (!entities.ContainsKey(entity)) { AddEntity(entity); } if (!entities.ContainsKey(entity)) { AddEntity(entity); }
entities[entity].Set(typeToIndex[typeof(TComponent)], true); entities[entity].Set(TypeToIndex[typeof(TComponent)], true);
} }
public void RemoveComponent<TComponent>(Entity entity) where TComponent : struct, IComponent public void RemoveComponent<TComponent>(Entity entity) where TComponent : struct, IComponent
{ {
entities[entity].Set(typeToIndex[typeof(TComponent)], false); entities[entity].Set(TypeToIndex[typeof(TComponent)], false);
} }
public void RemoveEntity(Entity entity) public void RemoveEntity(Entity entity)
@ -60,26 +64,9 @@ namespace Encompass
} }
} }
public IEnumerable<Entity> EntitiesWithComponents(IEnumerable<Type> types) public BitArray EntityBitArray(Entity entity)
{ {
foreach (var kvp in entities) return entities.ContainsKey(entity) ? entities[entity] : emptyArray;
{
queryArray.SetAll(false);
foreach (var type in types)
{
queryArray.Set(typeToIndex[type], true);
}
queryArray.And(kvp.Value);
var hasComponents = true;
foreach (var type in types)
{
if (!queryArray.Get(typeToIndex[type])) {
hasComponents = false;
break;
}
}
if (hasComponents) { yield return kvp.Key; }
}
} }
} }
} }

View File

@ -8,13 +8,14 @@ namespace Encompass
private readonly DrawLayerManager drawLayerManager; private readonly DrawLayerManager drawLayerManager;
private readonly ComponentUpdateManager componentUpdateManager; private readonly ComponentUpdateManager componentUpdateManager;
private readonly ComponentStore componentStore = new ComponentStore(); private readonly ComponentStore componentStore;
private readonly HashSet<Entity> entitiesMarkedForRemoval = new HashSet<Entity>(); private readonly HashSet<Entity> entitiesMarkedForRemoval = new HashSet<Entity>();
public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager) public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager, Dictionary<Type, int> typeToIndex)
{ {
this.drawLayerManager = drawLayerManager; this.drawLayerManager = drawLayerManager;
this.componentUpdateManager = componentUpdateManager; this.componentUpdateManager = componentUpdateManager;
componentStore = new ComponentStore(typeToIndex);
} }
public void RegisterComponentType<TComponent>() where TComponent : struct, IComponent public void RegisterComponentType<TComponent>() where TComponent : struct, IComponent
@ -96,10 +97,5 @@ namespace Encompass
componentUpdateManager.Remove<TComponent>(entity); componentUpdateManager.Remove<TComponent>(entity);
drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity); drawLayerManager.UnRegisterComponentWithLayer<TComponent>(entity);
} }
public IEnumerable<Entity> EntitiesWithComponents(IEnumerable<Type> types)
{
return componentStore.EntitiesWithComponents(types);
}
} }
} }

View File

@ -1,16 +1,27 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Encompass namespace Encompass
{ {
internal class ComponentUpdateManager internal class ComponentUpdateManager
{ {
private readonly ComponentStore existingAndPendingComponentStore = new ComponentStore(); private readonly ComponentStore existingAndPendingComponentStore;
private readonly ComponentStore existingComponentStore = new ComponentStore(); private readonly ComponentStore existingComponentStore;
private readonly ComponentStore pendingComponentStore = new ComponentStore(); private readonly ComponentStore pendingComponentStore;
private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToPendingComponentPriority = new Dictionary<Type, Dictionary<Entity, int>>(128); private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToPendingComponentPriority = new Dictionary<Type, Dictionary<Entity, int>>(128);
private Dictionary<Type, int> typeToIndex;
public ComponentStore UpToDateComponentStore { get; private set; }
public ComponentStore UpToDateComponentStore { get; private set; } = new ComponentStore(); public ComponentUpdateManager(Dictionary<Type, int> typeToIndex)
{
existingAndPendingComponentStore = new ComponentStore(typeToIndex);
existingComponentStore = new ComponentStore(typeToIndex);
pendingComponentStore = new ComponentStore(typeToIndex);
UpToDateComponentStore = new ComponentStore(typeToIndex);
this.typeToIndex = typeToIndex;
}
public void RegisterComponentType<TComponent>() where TComponent : struct, IComponent public void RegisterComponentType<TComponent>() where TComponent : struct, IComponent
{ {
@ -183,5 +194,63 @@ namespace Encompass
{ {
UpToDateComponentStore.Remove<TComponent>(entity); UpToDateComponentStore.Remove<TComponent>(entity);
} }
internal IEnumerable<Entity> QueryEntities(IEnumerable<Entity> entities, HashSet<Type> readTypes, HashSet<Type> readPendingTypes, IEnumerable<Type> withTypes, IEnumerable<Type> withoutTypes)
{
var pendingMask = new BitArray(typeToIndex.Count);
foreach (var type in readPendingTypes)
{
pendingMask.Set(typeToIndex[type], true);
}
var readMask = new BitArray(typeToIndex.Count);
foreach (var type in readTypes)
{
readMask.Set(typeToIndex[type], true);
}
var withMask = new BitArray(typeToIndex.Count);
foreach (var type in withTypes)
{
withMask.Set(typeToIndex[type], true);
}
var withoutMask = new BitArray(typeToIndex.Count);
foreach (var type in withoutTypes)
{
withoutMask.Set(typeToIndex[type], true);
}
foreach (var entity in entities)
{
var pendingEntity = pendingComponentStore.EntityBitArray(entity);
var existingEntity = existingComponentStore.EntityBitArray(entity);
if (VerifyTypes(pendingEntity, existingEntity, readMask, pendingMask, withMask, withoutMask)) { yield return entity; }
}
}
internal bool VerifyTypes(BitArray pendingEntity, BitArray existingEntity, BitArray readMask, BitArray pendingMask, BitArray withMask, BitArray withoutMask)
{
var arrayA = new BitArray(typeToIndex.Count);
var arrayB = new BitArray(typeToIndex.Count);
var arrayC = new BitArray(typeToIndex.Count);
var notWithMask = new BitArray(typeToIndex.Count);
notWithMask.Or(withMask).Not();
arrayA.Or(pendingMask).And(withMask).And(pendingEntity);
arrayB.Or(readMask).And(withMask).And(existingEntity);
arrayA.Or(arrayB).Or(notWithMask);
arrayB.SetAll(false);
arrayB.Or(pendingMask).And(withoutMask).And(pendingEntity).Not();
arrayC.Or(readMask).And(withoutMask).And(existingEntity).Not();
arrayB.And(arrayC);
arrayA.And(arrayB);
return !arrayA.Cast<bool>().Contains(false);
}
} }
} }

View File

@ -13,11 +13,12 @@ namespace Encompass
private readonly Dictionary<int, HashSet<GeneralRenderer>> layerIndexToGeneralRenderers = new Dictionary<int, HashSet<GeneralRenderer>>(512); private readonly Dictionary<int, HashSet<GeneralRenderer>> layerIndexToGeneralRenderers = new Dictionary<int, HashSet<GeneralRenderer>>(512);
private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToLayer = new Dictionary<Type, Dictionary<Entity, int>>(512); private readonly Dictionary<Type, Dictionary<Entity, int>> typeToEntityToLayer = new Dictionary<Type, Dictionary<Entity, int>>(512);
private Dictionary<Type, int> typeToIndex;
public IEnumerable<int> LayerOrder { get { return layerOrder.Values; } } public IEnumerable<int> LayerOrder { get { return layerOrder.Values; } }
public DrawLayerManager() public DrawLayerManager(Dictionary<Type, int> typeToIndex)
{ {
this.typeToIndex = typeToIndex;
RegisterDrawLayer(0); RegisterDrawLayer(0);
} }
@ -27,7 +28,15 @@ namespace Encompass
{ {
layerOrder.Add(layer, layer); layerOrder.Add(layer, layer);
layerIndexToGeneralRenderers.Add(layer, new HashSet<GeneralRenderer>()); layerIndexToGeneralRenderers.Add(layer, new HashSet<GeneralRenderer>());
layerIndexToComponentStore.Add(layer, new ComponentStore()); layerIndexToComponentStore.Add(layer, new ComponentStore(typeToIndex));
}
}
public void FinishRegistering()
{
foreach (var store in layerIndexToComponentStore.Values)
{
store.FinishRegistering();
} }
} }

View File

@ -612,9 +612,12 @@ namespace Encompass
timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction);
} }
/// <summary>
/// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria.
/// </summary>
protected EntitySetQuery QueryEntities() protected EntitySetQuery QueryEntities()
{ {
return new EntitySetQuery(componentManager); return new EntitySetQuery(entityManager, componentUpdateManager, readTypes, readPendingTypes);
} }
} }
} }

View File

@ -7,12 +7,17 @@ namespace Encompass
{ {
private readonly int entityCapacity; private readonly int entityCapacity;
private readonly IDManager idManager = new IDManager(); private readonly IDManager idManager = new IDManager();
private readonly HashSet<int> IDs = new HashSet<int>(); private readonly Dictionary<int, Entity> IDs = new Dictionary<int, Entity>(32768);
private readonly HashSet<Entity> entitiesMarkedForDestroy = new HashSet<Entity>(); private readonly HashSet<Entity> entitiesMarkedForDestroy = new HashSet<Entity>();
private readonly ComponentManager componentManager; private readonly ComponentManager componentManager;
public IEnumerable<Entity> Entities
{
get { return IDs.Values; }
}
public EntityManager(ComponentManager componentManager, int entityCapacity) public EntityManager(ComponentManager componentManager, int entityCapacity)
{ {
this.componentManager = componentManager; this.componentManager = componentManager;
@ -30,7 +35,7 @@ namespace Encompass
{ {
var id = NextID(); var id = NextID();
var entity = new Entity(id); var entity = new Entity(id);
IDs.Add(id); IDs.Add(id, entity);
return entity; return entity;
} }
else else
@ -41,7 +46,7 @@ namespace Encompass
public bool EntityExists(int id) public bool EntityExists(int id)
{ {
return IDs.Contains(id); return IDs.ContainsKey(id);
} }
public void MarkForDestroy(Entity entity) public void MarkForDestroy(Entity entity)

View File

@ -5,39 +5,57 @@ using System.Collections.Immutable;
namespace Encompass namespace Encompass
{ {
/// <summary>
/// EntitySetQuery is used to efficiently obtain a set of Entities that have all required Components and do not have any forbidden Components.
/// </summary>
public struct EntitySetQuery : IEnumerable<Entity> public struct EntitySetQuery : IEnumerable<Entity>
{ {
private EntitySetQuery(ComponentManager componentManager, ImmutableArray<Type> includes, ImmutableArray<Type> excludes) private EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet<Type> readTypes, HashSet<Type> readPendingTypes, ImmutableArray<Type> includes, ImmutableArray<Type> excludes)
{ {
ComponentManager = componentManager; EntityManager = entityManager;
ComponentUpdateManager = componentUpdateManager;
ReadTypes = readTypes;
ReadPendingTypes = readPendingTypes;
Includes = includes; Includes = includes;
Excludes = excludes; Excludes = excludes;
} }
internal EntitySetQuery(ComponentManager componentManager) internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet<Type> readTypes, HashSet<Type> readPendingTypes)
{ {
ComponentManager = componentManager; EntityManager = entityManager;
ComponentUpdateManager = componentUpdateManager;
ReadTypes = readTypes;
ReadPendingTypes = readPendingTypes;
Includes = ImmutableArray.Create<Type>(); Includes = ImmutableArray.Create<Type>();
Excludes = ImmutableArray.Create<Type>(); Excludes = ImmutableArray.Create<Type>();
} }
private ComponentManager ComponentManager { get; } private EntityManager EntityManager { get; }
private ComponentUpdateManager ComponentUpdateManager { get; }
private HashSet<Type> ReadTypes { get; }
private HashSet<Type> ReadPendingTypes { get; }
private ImmutableArray<Type> Includes { get; } private ImmutableArray<Type> Includes { get; }
private ImmutableArray<Type> Excludes { get; } private ImmutableArray<Type> Excludes { get; }
/// <summary>
/// Designates that the given component type is required.
/// </summary>
public EntitySetQuery With<TComponent>() where TComponent : struct, IComponent public EntitySetQuery With<TComponent>() where TComponent : struct, IComponent
{ {
return new EntitySetQuery(ComponentManager, Includes.Add(typeof(TComponent)), Excludes); return new EntitySetQuery(EntityManager, ComponentUpdateManager, ReadTypes, ReadPendingTypes, Includes.Add(typeof(TComponent)), Excludes);
} }
/// <summary>
/// Designates that the given component type is forbidden.
/// </summary>
public EntitySetQuery Without<TComponent>() where TComponent : struct, IComponent public EntitySetQuery Without<TComponent>() where TComponent : struct, IComponent
{ {
return new EntitySetQuery(ComponentManager, Includes, Excludes.Add(typeof(TComponent))); return new EntitySetQuery(EntityManager, ComponentUpdateManager, ReadTypes, ReadPendingTypes, Includes, Excludes.Add(typeof(TComponent)));
} }
public IEnumerator<Entity> GetEnumerator() public IEnumerator<Entity> GetEnumerator()
{ {
return ComponentManager.EntitiesWithComponents(Includes).GetEnumerator(); return ComponentUpdateManager.QueryEntities(EntityManager.Entities, ReadTypes, ReadPendingTypes, Includes, Excludes).GetEnumerator();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()

View File

@ -21,8 +21,8 @@ namespace Encompass
private readonly int entityCapacity; private readonly int entityCapacity;
private readonly List<Engine> engines = new List<Engine>(); private readonly List<Engine> engines = new List<Engine>();
private readonly DirectedGraph<Engine, Unit> engineGraph = GraphBuilder.DirectedGraph<Engine>(); private readonly DirectedGraph<Engine, Unit> engineGraph = GraphBuilder.DirectedGraph<Engine>();
private readonly ComponentStore startingComponentStoreForComponentManager = new ComponentStore(); private readonly ComponentStore startingComponentStoreForComponentManager;
private readonly ComponentStore startingComponentStoreForComponentUpdateManager = new ComponentStore(); private readonly ComponentStore startingComponentStoreForComponentUpdateManager;
private readonly ComponentManager componentManager; private readonly ComponentManager componentManager;
private readonly EntityManager entityManager; private readonly EntityManager entityManager;
@ -40,16 +40,21 @@ namespace Encompass
private readonly HashSet<Type> messageTypes = new HashSet<Type>(); private readonly HashSet<Type> messageTypes = new HashSet<Type>();
private readonly Dictionary<Type, int> typeToIndex = new Dictionary<Type, int>();
public WorldBuilder(int entityCapacity = 32768) public WorldBuilder(int entityCapacity = 32768)
{ {
this.entityCapacity = entityCapacity; this.entityCapacity = entityCapacity;
drawLayerManager = new DrawLayerManager(); drawLayerManager = new DrawLayerManager(typeToIndex);
timeManager = new TimeManager(); timeManager = new TimeManager();
componentUpdateManager = new ComponentUpdateManager(); componentUpdateManager = new ComponentUpdateManager(typeToIndex);
componentManager = new ComponentManager(drawLayerManager, componentUpdateManager); componentManager = new ComponentManager(drawLayerManager, componentUpdateManager, typeToIndex);
messageManager = new MessageManager(timeManager); messageManager = new MessageManager(timeManager);
entityManager = new EntityManager(componentManager, entityCapacity); entityManager = new EntityManager(componentManager, entityCapacity);
renderManager = new RenderManager(drawLayerManager); renderManager = new RenderManager(drawLayerManager);
startingComponentStoreForComponentManager = new ComponentStore(typeToIndex);
startingComponentStoreForComponentUpdateManager = new ComponentStore(typeToIndex);
} }
/// <summary> /// <summary>
@ -84,7 +89,9 @@ namespace Encompass
RegisterComponentType<TComponent>(); RegisterComponentType<TComponent>();
componentTypesToRegister.Add(typeof(TComponent)); componentTypesToRegister.Add(typeof(TComponent));
startingComponentStoreForComponentManager.FinishRegistering();
startingComponentStoreForComponentManager.Set(entity, component); startingComponentStoreForComponentManager.Set(entity, component);
startingComponentStoreForComponentUpdateManager.FinishRegistering();
startingComponentStoreForComponentUpdateManager.Set(entity, component); startingComponentStoreForComponentUpdateManager.Set(entity, component);
if (component is IDrawableComponent drawableComponent) if (component is IDrawableComponent drawableComponent)
@ -96,10 +103,14 @@ namespace Encompass
internal void RegisterComponentType<TComponent>() where TComponent : struct, IComponent internal void RegisterComponentType<TComponent>() where TComponent : struct, IComponent
{ {
componentManager.RegisterComponentType<TComponent>(); if (!typeToIndex.ContainsKey(typeof(TComponent)))
componentUpdateManager.RegisterComponentType<TComponent>(); {
startingComponentStoreForComponentManager.RegisterComponentType<TComponent>(); typeToIndex.Add(typeof(TComponent), typeToIndex.Count);
startingComponentStoreForComponentUpdateManager.RegisterComponentType<TComponent>(); componentManager.RegisterComponentType<TComponent>();
componentUpdateManager.RegisterComponentType<TComponent>();
startingComponentStoreForComponentManager.RegisterComponentType<TComponent>();
startingComponentStoreForComponentUpdateManager.RegisterComponentType<TComponent>();
}
} }
internal void RegisterMessageTypes(IEnumerable<Type> types) internal void RegisterMessageTypes(IEnumerable<Type> types)
@ -363,13 +374,14 @@ namespace Encompass
engineOrder.Add(emitterEngine); engineOrder.Add(emitterEngine);
} }
PreloadJIT(componentTypesToRegister, messageTypes);
componentManager.FinishRegistering(); componentManager.FinishRegistering();
componentUpdateManager.FinishRegistering(); componentUpdateManager.FinishRegistering();
drawLayerManager.FinishRegistering();
startingComponentStoreForComponentManager.FinishRegistering(); startingComponentStoreForComponentManager.FinishRegistering();
startingComponentStoreForComponentUpdateManager.FinishRegistering(); startingComponentStoreForComponentUpdateManager.FinishRegistering();
PreloadJIT(componentTypesToRegister, messageTypes);
foreach (var engine in engineGraph.TopologicalSort()) foreach (var engine in engineGraph.TopologicalSort())
{ {
engineOrder.Add(engine); engineOrder.Add(engine);
@ -401,9 +413,9 @@ namespace Encompass
{ {
var dummyTimeManager = new TimeManager(); var dummyTimeManager = new TimeManager();
var dummyMessageManager = new MessageManager(dummyTimeManager); var dummyMessageManager = new MessageManager(dummyTimeManager);
var dummyDrawLayerManager = new DrawLayerManager(); var dummyDrawLayerManager = new DrawLayerManager(typeToIndex);
var dummyComponentUpdateManager = new ComponentUpdateManager(); var dummyComponentUpdateManager = new ComponentUpdateManager(typeToIndex);
var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager); var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager, typeToIndex);
var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity);
var dummyRenderManager = new RenderManager(dummyDrawLayerManager); var dummyRenderManager = new RenderManager(dummyDrawLayerManager);

View File

@ -1088,42 +1088,156 @@ namespace Tests
undilatedDeltaTime.Should().Be(0.5); undilatedDeltaTime.Should().Be(0.5);
} }
struct MockComponentB : IComponent { } public class QueryTests
static Entity[] queriedEntities;
class EntityQueryEngine : Engine
{ {
public override void Update(double dt) struct MockComponentB : IComponent { }
struct MockComponentC : IComponent { }
struct MockComponentD : IComponent { }
[Reads(typeof(MockComponent), typeof(MockComponentB))]
class EntityQueryWithComponentsEngine : Engine
{ {
queriedEntities = QueryEntities().With<MockComponent>().With<MockComponentB>().ToArray(); private List<Entity> entities;
public EntityQueryWithComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().With<MockComponent>().With<MockComponentB>());
}
} }
}
[Test] [Test]
public void EntitiesWithComponents() public void EntitiesWithComponents()
{ {
var worldBuilder = new WorldBuilder(); var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity(); var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity(); var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity(); var entityC = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent()); worldBuilder.SetComponent(entity, new MockComponent());
worldBuilder.SetComponent(entity, new MockComponentB()); worldBuilder.SetComponent(entity, new MockComponentB());
worldBuilder.SetComponent(entityB, new MockComponent()); worldBuilder.SetComponent(entityB, new MockComponent());
worldBuilder.SetComponent(entityB, new MockComponentB()); worldBuilder.SetComponent(entityB, new MockComponentB());
worldBuilder.SetComponent(entityC, new MockComponentB()); worldBuilder.SetComponent(entityC, new MockComponentB());
worldBuilder.AddEngine(new EntityQueryEngine()); var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new EntityQueryWithComponentsEngine(queriedEntities));
var world = worldBuilder.Build(); var world = worldBuilder.Build();
world.Update(0.01); world.Update(0.01);
queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB }); queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB });
}
[Reads(typeof(MockComponent))]
class EntityQueryWithoutComponentsEngine : Engine
{
private List<Entity> entities;
public EntityQueryWithoutComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().Without<MockComponent>());
}
}
[Test]
public void EntitiesWithoutComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent());
worldBuilder.SetComponent(entity, new MockComponentB());
worldBuilder.SetComponent(entityB, new MockComponent());
worldBuilder.SetComponent(entityB, new MockComponentB());
worldBuilder.SetComponent(entityC, new MockComponentB());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new EntityQueryWithoutComponentsEngine(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityC });
}
[Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))]
class EntityQueryWithandWithoutComponentsEngine : Engine
{
private List<Entity> entities;
public EntityQueryWithandWithoutComponentsEngine(List<Entity> entities)
{
this.entities = entities;
}
public override void Update(double dt)
{
entities.Clear();
entities.AddRange(QueryEntities().With<MockComponent>()
.With<MockComponentB>()
.Without<MockComponentD>());
}
}
[Test]
public void EntitiesWithAndWithoutComponents()
{
var worldBuilder = new WorldBuilder();
var entity = worldBuilder.CreateEntity();
var entityB = worldBuilder.CreateEntity();
var entityC = worldBuilder.CreateEntity();
var entityD = worldBuilder.CreateEntity();
worldBuilder.SetComponent(entity, new MockComponent());
worldBuilder.SetComponent(entity, new MockComponentB());
worldBuilder.SetComponent(entity, new MockComponentD());
worldBuilder.SetComponent(entityB, new MockComponent());
worldBuilder.SetComponent(entityC, new MockComponent());
worldBuilder.SetComponent(entityC, new MockComponentB());
worldBuilder.SetComponent(entityC, new MockComponentC());
worldBuilder.SetComponent(entityC, new MockComponentD());
worldBuilder.SetComponent(entityD, new MockComponent());
worldBuilder.SetComponent(entityD, new MockComponentB());
worldBuilder.SetComponent(entityD, new MockComponentC());
var queriedEntities = new List<Entity>();
worldBuilder.AddEngine(new EntityQueryWithandWithoutComponentsEngine(queriedEntities));
var world = worldBuilder.Build();
world.Update(0.01);
queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityD });
}
} }
} }
} }