From 50974c181a6c51bfdd992b64742a5fafc7a476b1 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Sun, 22 Dec 2019 01:15:58 -0800 Subject: [PATCH] advanced query system --- encompass-cs/Collections/ComponentStore.cs | 13 +- encompass-cs/ComponentBitSet.cs | 49 +++---- encompass-cs/ComponentManager.cs | 10 +- encompass-cs/ComponentUpdateManager.cs | 77 +++++++++- encompass-cs/DrawLayerManager.cs | 15 +- encompass-cs/Engine.cs | 5 +- encompass-cs/EntityManager.cs | 11 +- encompass-cs/EntitySetQuery.cs | 34 ++++- encompass-cs/WorldBuilder.cs | 40 +++-- test/EngineTest.cs | 162 ++++++++++++++++++--- 10 files changed, 317 insertions(+), 99 deletions(-) diff --git a/encompass-cs/Collections/ComponentStore.cs b/encompass-cs/Collections/ComponentStore.cs index 88c4d12..4a95184 100644 --- a/encompass-cs/Collections/ComponentStore.cs +++ b/encompass-cs/Collections/ComponentStore.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; namespace Encompass @@ -6,7 +7,12 @@ namespace Encompass internal class ComponentStore { private Dictionary Stores = new Dictionary(512); - private ComponentBitSet componentBitSet = new ComponentBitSet(); + private ComponentBitSet componentBitSet; + + public ComponentStore(Dictionary typeToIndex) + { + componentBitSet = new ComponentBitSet(typeToIndex); + } public IEnumerable<(Type, TypedComponentStore)> StoresEnumerable() { @@ -22,7 +28,6 @@ namespace Encompass { var store = new TypedComponentStore(); Stores.Add(typeof(TComponent), store); - componentBitSet.RegisterType(); } } @@ -47,9 +52,9 @@ namespace Encompass return Stores.ContainsKey(type) && Stores[type].Has(entity); } - public IEnumerable EntitiesWithComponents(IEnumerable types) + public BitArray EntityBitArray(Entity entity) { - return componentBitSet.EntitiesWithComponents(types); + return componentBitSet.EntityBitArray(entity); } public TComponent Get(Entity entity) where TComponent : struct, IComponent diff --git a/encompass-cs/ComponentBitSet.cs b/encompass-cs/ComponentBitSet.cs index 38c0000..7ec25f4 100644 --- a/encompass-cs/ComponentBitSet.cs +++ b/encompass-cs/ComponentBitSet.cs @@ -8,21 +8,25 @@ namespace Encompass { BitArrayPool bitArrayPool = new BitArrayPool(32768); // todo: set entity cap Dictionary entities = new Dictionary(); - Dictionary typeToIndex = new Dictionary(); - BitArray queryArray; + Dictionary TypeToIndex { get; set; } + BitArray withQueryArray; + BitArray withoutQueryArray; + BitArray emptyArray; - public void RegisterType() where TComponent : struct, IComponent + public ComponentBitSet(Dictionary typeToIndex) { - typeToIndex.Add(typeof(TComponent), typeToIndex.Count); - foreach (var kvp in entities) - { - kvp.Value.Length = typeToIndex.Count; - } + TypeToIndex = typeToIndex; } 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() @@ -36,19 +40,19 @@ namespace Encompass public void AddEntity(Entity entity) { - var bitArray = bitArrayPool.Obtain(typeToIndex.Count); + var bitArray = bitArrayPool.Obtain(TypeToIndex.Count); entities.Add(entity, bitArray); } public void Set(Entity entity) where TComponent : struct, IComponent { if (!entities.ContainsKey(entity)) { AddEntity(entity); } - entities[entity].Set(typeToIndex[typeof(TComponent)], true); + entities[entity].Set(TypeToIndex[typeof(TComponent)], true); } public void RemoveComponent(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) @@ -60,26 +64,9 @@ namespace Encompass } } - public IEnumerable EntitiesWithComponents(IEnumerable types) + public BitArray EntityBitArray(Entity entity) { - foreach (var kvp in entities) - { - 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; } - } + return entities.ContainsKey(entity) ? entities[entity] : emptyArray; } } } diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index bc8f304..30d024b 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -8,13 +8,14 @@ namespace Encompass private readonly DrawLayerManager drawLayerManager; private readonly ComponentUpdateManager componentUpdateManager; - private readonly ComponentStore componentStore = new ComponentStore(); + private readonly ComponentStore componentStore; private readonly HashSet entitiesMarkedForRemoval = new HashSet(); - public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager) + public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager, Dictionary typeToIndex) { this.drawLayerManager = drawLayerManager; this.componentUpdateManager = componentUpdateManager; + componentStore = new ComponentStore(typeToIndex); } public void RegisterComponentType() where TComponent : struct, IComponent @@ -96,10 +97,5 @@ namespace Encompass componentUpdateManager.Remove(entity); drawLayerManager.UnRegisterComponentWithLayer(entity); } - - public IEnumerable EntitiesWithComponents(IEnumerable types) - { - return componentStore.EntitiesWithComponents(types); - } } } diff --git a/encompass-cs/ComponentUpdateManager.cs b/encompass-cs/ComponentUpdateManager.cs index 5c01b96..3b270db 100644 --- a/encompass-cs/ComponentUpdateManager.cs +++ b/encompass-cs/ComponentUpdateManager.cs @@ -1,16 +1,27 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; namespace Encompass { internal class ComponentUpdateManager { - private readonly ComponentStore existingAndPendingComponentStore = new ComponentStore(); - private readonly ComponentStore existingComponentStore = new ComponentStore(); - private readonly ComponentStore pendingComponentStore = new ComponentStore(); + private readonly ComponentStore existingAndPendingComponentStore; + private readonly ComponentStore existingComponentStore; + private readonly ComponentStore pendingComponentStore; private readonly Dictionary> typeToEntityToPendingComponentPriority = new Dictionary>(128); + private Dictionary typeToIndex; + public ComponentStore UpToDateComponentStore { get; private set; } - public ComponentStore UpToDateComponentStore { get; private set; } = new ComponentStore(); + public ComponentUpdateManager(Dictionary typeToIndex) + { + existingAndPendingComponentStore = new ComponentStore(typeToIndex); + existingComponentStore = new ComponentStore(typeToIndex); + pendingComponentStore = new ComponentStore(typeToIndex); + UpToDateComponentStore = new ComponentStore(typeToIndex); + this.typeToIndex = typeToIndex; + } public void RegisterComponentType() where TComponent : struct, IComponent { @@ -183,5 +194,63 @@ namespace Encompass { UpToDateComponentStore.Remove(entity); } + + internal IEnumerable QueryEntities(IEnumerable entities, HashSet readTypes, HashSet readPendingTypes, IEnumerable withTypes, IEnumerable 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().Contains(false); + } } } diff --git a/encompass-cs/DrawLayerManager.cs b/encompass-cs/DrawLayerManager.cs index 7c9a68f..65cde48 100644 --- a/encompass-cs/DrawLayerManager.cs +++ b/encompass-cs/DrawLayerManager.cs @@ -13,11 +13,12 @@ namespace Encompass private readonly Dictionary> layerIndexToGeneralRenderers = new Dictionary>(512); private readonly Dictionary> typeToEntityToLayer = new Dictionary>(512); - + private Dictionary typeToIndex; public IEnumerable LayerOrder { get { return layerOrder.Values; } } - public DrawLayerManager() + public DrawLayerManager(Dictionary typeToIndex) { + this.typeToIndex = typeToIndex; RegisterDrawLayer(0); } @@ -27,7 +28,15 @@ namespace Encompass { layerOrder.Add(layer, layer); layerIndexToGeneralRenderers.Add(layer, new HashSet()); - layerIndexToComponentStore.Add(layer, new ComponentStore()); + layerIndexToComponentStore.Add(layer, new ComponentStore(typeToIndex)); + } + } + + public void FinishRegistering() + { + foreach (var store in layerIndexToComponentStore.Values) + { + store.FinishRegistering(); } } diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index cd17789..457c6f2 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -612,9 +612,12 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } + /// + /// Returns an empty EntitySetQuery. Can be modified and iterated over to obtain Entities that fit the given criteria. + /// protected EntitySetQuery QueryEntities() { - return new EntitySetQuery(componentManager); + return new EntitySetQuery(entityManager, componentUpdateManager, readTypes, readPendingTypes); } } } diff --git a/encompass-cs/EntityManager.cs b/encompass-cs/EntityManager.cs index a264046..36650c5 100644 --- a/encompass-cs/EntityManager.cs +++ b/encompass-cs/EntityManager.cs @@ -7,12 +7,17 @@ namespace Encompass { private readonly int entityCapacity; private readonly IDManager idManager = new IDManager(); - private readonly HashSet IDs = new HashSet(); + private readonly Dictionary IDs = new Dictionary(32768); private readonly HashSet entitiesMarkedForDestroy = new HashSet(); private readonly ComponentManager componentManager; + public IEnumerable Entities + { + get { return IDs.Values; } + } + public EntityManager(ComponentManager componentManager, int entityCapacity) { this.componentManager = componentManager; @@ -30,7 +35,7 @@ namespace Encompass { var id = NextID(); var entity = new Entity(id); - IDs.Add(id); + IDs.Add(id, entity); return entity; } else @@ -41,7 +46,7 @@ namespace Encompass public bool EntityExists(int id) { - return IDs.Contains(id); + return IDs.ContainsKey(id); } public void MarkForDestroy(Entity entity) diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index 25d5b7e..3fd2e59 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -5,39 +5,57 @@ using System.Collections.Immutable; namespace Encompass { + /// + /// EntitySetQuery is used to efficiently obtain a set of Entities that have all required Components and do not have any forbidden Components. + /// public struct EntitySetQuery : IEnumerable { - private EntitySetQuery(ComponentManager componentManager, ImmutableArray includes, ImmutableArray excludes) + private EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet readTypes, HashSet readPendingTypes, ImmutableArray includes, ImmutableArray excludes) { - ComponentManager = componentManager; + EntityManager = entityManager; + ComponentUpdateManager = componentUpdateManager; + ReadTypes = readTypes; + ReadPendingTypes = readPendingTypes; Includes = includes; Excludes = excludes; } - internal EntitySetQuery(ComponentManager componentManager) + internal EntitySetQuery(EntityManager entityManager, ComponentUpdateManager componentUpdateManager, HashSet readTypes, HashSet readPendingTypes) { - ComponentManager = componentManager; + EntityManager = entityManager; + ComponentUpdateManager = componentUpdateManager; + ReadTypes = readTypes; + ReadPendingTypes = readPendingTypes; Includes = ImmutableArray.Create(); Excludes = ImmutableArray.Create(); } - private ComponentManager ComponentManager { get; } + private EntityManager EntityManager { get; } + private ComponentUpdateManager ComponentUpdateManager { get; } + private HashSet ReadTypes { get; } + private HashSet ReadPendingTypes { get; } private ImmutableArray Includes { get; } private ImmutableArray Excludes { get; } + /// + /// Designates that the given component type is required. + /// public EntitySetQuery With() 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); } + /// + /// Designates that the given component type is forbidden. + /// public EntitySetQuery Without() 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 GetEnumerator() { - return ComponentManager.EntitiesWithComponents(Includes).GetEnumerator(); + return ComponentUpdateManager.QueryEntities(EntityManager.Entities, ReadTypes, ReadPendingTypes, Includes, Excludes).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index cae7d70..1e88a88 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -21,8 +21,8 @@ namespace Encompass private readonly int entityCapacity; private readonly List engines = new List(); private readonly DirectedGraph engineGraph = GraphBuilder.DirectedGraph(); - private readonly ComponentStore startingComponentStoreForComponentManager = new ComponentStore(); - private readonly ComponentStore startingComponentStoreForComponentUpdateManager = new ComponentStore(); + private readonly ComponentStore startingComponentStoreForComponentManager; + private readonly ComponentStore startingComponentStoreForComponentUpdateManager; private readonly ComponentManager componentManager; private readonly EntityManager entityManager; @@ -40,16 +40,21 @@ namespace Encompass private readonly HashSet messageTypes = new HashSet(); + private readonly Dictionary typeToIndex = new Dictionary(); + public WorldBuilder(int entityCapacity = 32768) { this.entityCapacity = entityCapacity; - drawLayerManager = new DrawLayerManager(); + drawLayerManager = new DrawLayerManager(typeToIndex); timeManager = new TimeManager(); - componentUpdateManager = new ComponentUpdateManager(); - componentManager = new ComponentManager(drawLayerManager, componentUpdateManager); + componentUpdateManager = new ComponentUpdateManager(typeToIndex); + componentManager = new ComponentManager(drawLayerManager, componentUpdateManager, typeToIndex); messageManager = new MessageManager(timeManager); entityManager = new EntityManager(componentManager, entityCapacity); renderManager = new RenderManager(drawLayerManager); + + startingComponentStoreForComponentManager = new ComponentStore(typeToIndex); + startingComponentStoreForComponentUpdateManager = new ComponentStore(typeToIndex); } /// @@ -84,7 +89,9 @@ namespace Encompass RegisterComponentType(); componentTypesToRegister.Add(typeof(TComponent)); + startingComponentStoreForComponentManager.FinishRegistering(); startingComponentStoreForComponentManager.Set(entity, component); + startingComponentStoreForComponentUpdateManager.FinishRegistering(); startingComponentStoreForComponentUpdateManager.Set(entity, component); if (component is IDrawableComponent drawableComponent) @@ -96,10 +103,14 @@ namespace Encompass internal void RegisterComponentType() where TComponent : struct, IComponent { - componentManager.RegisterComponentType(); - componentUpdateManager.RegisterComponentType(); - startingComponentStoreForComponentManager.RegisterComponentType(); - startingComponentStoreForComponentUpdateManager.RegisterComponentType(); + if (!typeToIndex.ContainsKey(typeof(TComponent))) + { + typeToIndex.Add(typeof(TComponent), typeToIndex.Count); + componentManager.RegisterComponentType(); + componentUpdateManager.RegisterComponentType(); + startingComponentStoreForComponentManager.RegisterComponentType(); + startingComponentStoreForComponentUpdateManager.RegisterComponentType(); + } } internal void RegisterMessageTypes(IEnumerable types) @@ -363,13 +374,14 @@ namespace Encompass engineOrder.Add(emitterEngine); } - PreloadJIT(componentTypesToRegister, messageTypes); - componentManager.FinishRegistering(); componentUpdateManager.FinishRegistering(); + drawLayerManager.FinishRegistering(); startingComponentStoreForComponentManager.FinishRegistering(); startingComponentStoreForComponentUpdateManager.FinishRegistering(); + PreloadJIT(componentTypesToRegister, messageTypes); + foreach (var engine in engineGraph.TopologicalSort()) { engineOrder.Add(engine); @@ -401,9 +413,9 @@ namespace Encompass { var dummyTimeManager = new TimeManager(); var dummyMessageManager = new MessageManager(dummyTimeManager); - var dummyDrawLayerManager = new DrawLayerManager(); - var dummyComponentUpdateManager = new ComponentUpdateManager(); - var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager); + var dummyDrawLayerManager = new DrawLayerManager(typeToIndex); + var dummyComponentUpdateManager = new ComponentUpdateManager(typeToIndex); + var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager, typeToIndex); var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); var dummyRenderManager = new RenderManager(dummyDrawLayerManager); diff --git a/test/EngineTest.cs b/test/EngineTest.cs index a82af05..7d5b73d 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1088,42 +1088,156 @@ namespace Tests undilatedDeltaTime.Should().Be(0.5); } - struct MockComponentB : IComponent { } - - static Entity[] queriedEntities; - - class EntityQueryEngine : Engine + public class QueryTests { - 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().With().ToArray(); + private List entities; + + public EntityQueryWithComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().With().With()); + } } - } - [Test] - public void EntitiesWithComponents() - { - var worldBuilder = new WorldBuilder(); + [Test] + public void EntitiesWithComponents() + { + var worldBuilder = new WorldBuilder(); - var entity = worldBuilder.CreateEntity(); - var entityB = worldBuilder.CreateEntity(); - var entityC = worldBuilder.CreateEntity(); + var entity = worldBuilder.CreateEntity(); + var entityB = worldBuilder.CreateEntity(); + var entityC = worldBuilder.CreateEntity(); - worldBuilder.SetComponent(entity, new MockComponent()); - worldBuilder.SetComponent(entity, new MockComponentB()); + worldBuilder.SetComponent(entity, new MockComponent()); + worldBuilder.SetComponent(entity, new MockComponentB()); - worldBuilder.SetComponent(entityB, new MockComponent()); - worldBuilder.SetComponent(entityB, new MockComponentB()); + worldBuilder.SetComponent(entityB, new MockComponent()); + worldBuilder.SetComponent(entityB, new MockComponentB()); - worldBuilder.SetComponent(entityC, new MockComponentB()); + worldBuilder.SetComponent(entityC, new MockComponentB()); - worldBuilder.AddEngine(new EntityQueryEngine()); + var queriedEntities = new List(); + 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 entities; + + public EntityQueryWithoutComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().Without()); + } + } + + [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(); + 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 entities; + + public EntityQueryWithandWithoutComponentsEngine(List entities) + { + this.entities = entities; + } + + public override void Update(double dt) + { + entities.Clear(); + + entities.AddRange(QueryEntities().With() + .With() + .Without()); + } + } + + [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(); + worldBuilder.AddEngine(new EntityQueryWithandWithoutComponentsEngine(queriedEntities)); + + var world = worldBuilder.Build(); + + world.Update(0.01); + + queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityD }); + } } } }