diff --git a/encompass-cs/ComponentManager.cs b/encompass-cs/ComponentManager.cs index 5518bba..47935bc 100644 --- a/encompass-cs/ComponentManager.cs +++ b/encompass-cs/ComponentManager.cs @@ -11,6 +11,8 @@ namespace Encompass private readonly ComponentStore componentStore; private readonly HashSet entitiesMarkedForRemoval = new HashSet(); + internal ComponentBitSet ComponentBitSet { get { return componentStore.ComponentBitSet; } } + public ComponentManager(DrawLayerManager drawLayerManager, ComponentUpdateManager componentUpdateManager, Dictionary typeToIndex) { this.drawLayerManager = drawLayerManager; diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 79e8694..8bda3e9 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -38,9 +38,13 @@ namespace Encompass private ComponentManager componentManager; private ComponentUpdateManager componentUpdateManager; private TimeManager timeManager; + private TrackingManager trackingManager; private EntitySetQuery entityQuery; + private HashSet _trackedEntities = new HashSet(); + protected IEnumerable TrackedEntities { get { return _trackedEntities; } } + protected Engine() { ID = Guid.NewGuid(); @@ -146,6 +150,11 @@ namespace Encompass this.timeManager = timeManager; } + internal void AssignTrackingManager(TrackingManager trackingManager) + { + this.trackingManager = trackingManager; + } + /// /// Runs once per World update with the calculated delta-time. /// @@ -422,13 +431,18 @@ namespace Encompass bool written; if (writeImmediateTypes.Contains(typeof(TComponent))) { - written = AddImmediateComponent(entity, component, priority); + written = componentUpdateManager.AddImmediateComponent(entity, component, priority); } else { written = componentUpdateManager.UpdateComponent(entity, component, priority); } + if (!componentUpdateManager.HasExistingComponent(entity)) + { + trackingManager.RegisterAddition(entity, typeof(TComponent)); + } + if (written && component is IDrawableComponent drawableComponent) { componentManager.RegisterDrawableComponent(entity, component, drawableComponent.Layer); @@ -474,11 +488,6 @@ namespace Encompass componentUpdateManager.AddExistingComponent(entity, component); } - internal bool AddImmediateComponent(Entity entity, TComponent component, int priority) where TComponent : struct, IComponent - { - return componentUpdateManager.AddImmediateComponent(entity, component, priority); - } - /// /// Reads all messages of the specified Type. /// @@ -574,6 +583,11 @@ namespace Encompass { componentManager.Remove(entity, priority); } + + if (componentUpdateManager.HasExistingComponent(entity)) + { + trackingManager.RegisterRemoval(entity, typeof(TComponent)); + } } /// @@ -636,11 +650,19 @@ namespace Encompass timeManager.ActivateTimeDilation(factor, easeInTime, easeInFunction, activeTime, easeOutTime, easeOutFunction); } - protected IEnumerable QueryEntities() + internal void CheckTrackEntity(Entity entity) { - foreach (var entity in entityQuery.FilterEntities(entityManager.Entities, componentUpdateManager.ImmediateBits, componentUpdateManager.ExistingBits)) + if (entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) { - yield return entity; + _trackedEntities.Add(entity); + } + } + + internal void CheckUntrackEntity(Entity entity) + { + if (!entityQuery.CheckEntity(entity, componentManager.ComponentBitSet)) + { + _trackedEntities.Remove(entity); } } diff --git a/encompass-cs/EntitySetQuery.cs b/encompass-cs/EntitySetQuery.cs index afcd9ae..663766f 100644 --- a/encompass-cs/EntitySetQuery.cs +++ b/encompass-cs/EntitySetQuery.cs @@ -21,23 +21,30 @@ namespace Encompass NotWithMask = notWithMask; } - public IEnumerable FilterEntities(IEnumerable entities, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) + public bool CheckEntity(Entity entity, ComponentBitSet componentBitSet) { - foreach (var entity in entities) - { - var immediateBits = immediateBitLookup.EntityBitArray(entity.ID); - var existingBits = existingBitLookup.EntityBitArray(entity.ID); + var existingBits = componentBitSet.EntityBitArray(entity.ID); + var existing = WithExistingMask.And(existingBits).Or(NotWithMask); - var immediate = WithImmediateMask.And(immediateBits); - var existing = WithExistingMask.And(existingBits); - var withCheck = immediate.Or(existing).Or(NotWithMask); + var existingForbidden = WithoutExistingMask.And(existingBits).Not(); - var immediateForbidden = WithoutImmediateMask.And(immediateBits).Not(); - var existingForbidden = WithoutExistingMask.And(existingBits).Not(); - var withoutCheck = immediateForbidden.And(existingForbidden); + return existing.And(existingForbidden).AllTrue(); + } - if (withCheck.And(withoutCheck).AllTrue()) { yield return entity; } - } + public bool CheckEntityImmediate(Entity entity, ComponentBitSet immediateBitLookup, ComponentBitSet existingBitLookup) + { + var immediateBits = immediateBitLookup.EntityBitArray(entity.ID); + var existingBits = existingBitLookup.EntityBitArray(entity.ID); + + var immediate = WithImmediateMask.And(immediateBits); + var existing = WithExistingMask.And(existingBits); + var withCheck = immediate.Or(existing).Or(NotWithMask); + + var immediateForbidden = WithoutImmediateMask.And(immediateBits).Not(); + var existingForbidden = WithoutExistingMask.And(existingBits).Not(); + var withoutCheck = immediateForbidden.And(existingForbidden); + + return withCheck.And(withoutCheck).AllTrue(); } } } diff --git a/encompass-cs/TrackingManager.cs b/encompass-cs/TrackingManager.cs new file mode 100644 index 0000000..35eaf2d --- /dev/null +++ b/encompass-cs/TrackingManager.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; + +namespace Encompass +{ + internal class TrackingManager + { + private Dictionary> _pendingComponentTypesToEngines = new Dictionary>(); + private Dictionary> _componentTypesToEngines = new Dictionary>(); + + private HashSet<(Entity, Type)> _additions = new HashSet<(Entity, Type)>(); + private HashSet<(Entity, Type)> _removals = new HashSet<(Entity, Type)>(); + + public void RegisterComponentTypeToEngine(Type type, Engine engine) + { + if (!_componentTypesToEngines.ContainsKey(type)) { _componentTypesToEngines.Add(type, new HashSet()); } + _componentTypesToEngines[type].Add(engine); + } + + public void RegisterAddition(Entity entity, Type type) + { + _additions.Add((entity, type)); + } + + public void RegisterRemoval(Entity entity, Type type) + { + _removals.Add((entity, type)); + } + + public void InitializeTracking(IEnumerable entities) + { + foreach (var entity in entities) + { + foreach (var engineSet in _componentTypesToEngines.Values) + { + foreach (var engine in engineSet) + { + engine.CheckTrackEntity(entity); + } + } + } + } + + public void UpdateTracking() + { + // TODO: optimize so we only check each entity/engine pair once + foreach (var (entity, componentType) in _additions) + { + if (_componentTypesToEngines.ContainsKey(componentType)) + { + foreach (var engine in _componentTypesToEngines[componentType]) + { + engine.CheckTrackEntity(entity); + } + } + } + _additions.Clear(); + + foreach (var (entity, componentType) in _removals) + { + if (_componentTypesToEngines.ContainsKey(componentType)) + { + foreach (var engine in _componentTypesToEngines[componentType]) + { + engine.CheckUntrackEntity(entity); + } + } + } + _removals.Clear(); + } + } +} diff --git a/encompass-cs/World.cs b/encompass-cs/World.cs index 1154ca3..0e3707b 100644 --- a/encompass-cs/World.cs +++ b/encompass-cs/World.cs @@ -10,6 +10,7 @@ namespace Encompass private readonly List enginesInOrder; private readonly EntityManager entityManager; private readonly ComponentManager componentManager; + private readonly TrackingManager trackingManager; private readonly MessageManager messageManager; private readonly ComponentUpdateManager componentUpdateManager; private readonly TimeManager timeManager; @@ -19,6 +20,7 @@ namespace Encompass List enginesInOrder, EntityManager entityManager, ComponentManager componentManager, + TrackingManager trackingManager, MessageManager messageManager, ComponentUpdateManager componentUpdateManager, TimeManager timeManager, @@ -28,6 +30,7 @@ namespace Encompass this.enginesInOrder = enginesInOrder; this.entityManager = entityManager; this.componentManager = componentManager; + this.trackingManager = trackingManager; this.messageManager = messageManager; this.componentUpdateManager = componentUpdateManager; this.timeManager = timeManager; @@ -40,6 +43,7 @@ namespace Encompass /// The time in seconds that has passed since the previous frame. public void Update(double dt) { + trackingManager.UpdateTracking(); messageManager.ProcessDelayedMessages(dt); timeManager.Update(dt); diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index e18bec1..98326e5 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -31,6 +31,7 @@ namespace Encompass private readonly TimeManager timeManager; private readonly DrawLayerManager drawLayerManager; private readonly RenderManager renderManager; + private readonly TrackingManager trackingManager; private readonly Dictionary> typeToReaders = new Dictionary>(); @@ -52,6 +53,7 @@ namespace Encompass messageManager = new MessageManager(timeManager); entityManager = new EntityManager(componentManager, entityCapacity); renderManager = new RenderManager(entityManager, drawLayerManager); + trackingManager = new TrackingManager(); startingComponentStoreForComponentManager = new ComponentStore(typeToIndex); startingComponentStoreForComponentUpdateManager = new ComponentStore(typeToIndex); @@ -132,6 +134,7 @@ namespace Encompass engine.AssignMessageManager(messageManager); engine.AssignComponentUpdateManager(componentUpdateManager); engine.AssignTimeManager(timeManager); + engine.AssignTrackingManager(trackingManager); engines.Add(engine); engineGraph.AddNode(engine); @@ -156,6 +159,11 @@ namespace Encompass senders.Add(engine); } + foreach (var componentType in engine.queryWithTypes.Union(engine.queryWithoutTypes)) + { + trackingManager.RegisterComponentTypeToEngine(componentType, engine); + } + foreach (var componentType in engine.readTypes.Union(engine.writeTypes).Union(engine.readImmediateTypes)) { AddComponentTypeToRegister(componentType); @@ -381,6 +389,7 @@ namespace Encompass engineOrder, entityManager, componentManager, + trackingManager, messageManager, componentUpdateManager, timeManager, @@ -390,6 +399,8 @@ namespace Encompass componentUpdateManager.SetStartingComponentStore(startingComponentStoreForComponentUpdateManager); componentManager.SetComponentStore(startingComponentStoreForComponentManager); + trackingManager.InitializeTracking(entityManager.Entities); + return world; } @@ -406,6 +417,7 @@ namespace Encompass var dummyDrawLayerManager = new DrawLayerManager(typeToIndex); var dummyComponentUpdateManager = new ComponentUpdateManager(typeToIndex); var dummyComponentManager = new ComponentManager(dummyDrawLayerManager, dummyComponentUpdateManager, typeToIndex); + var dummyTrackingManager = new TrackingManager(); var dummyEntityManager = new EntityManager(dummyComponentManager, entityCapacity); var dummyRenderManager = new RenderManager(dummyEntityManager, dummyDrawLayerManager); @@ -418,6 +430,7 @@ namespace Encompass uberEngine.AssignMessageManager(dummyMessageManager); uberEngine.AssignComponentUpdateManager(dummyComponentUpdateManager); uberEngine.AssignTimeManager(dummyTimeManager); + uberEngine.AssignTrackingManager(dummyTrackingManager); var uberRenderer = new UberRenderer(componentTypes); uberRenderer.AssignComponentManager(dummyComponentManager); @@ -446,6 +459,7 @@ namespace Encompass emitterEngine.AssignMessageManager(dummyMessageManager); emitterEngine.AssignComponentUpdateManager(dummyComponentUpdateManager); emitterEngine.AssignTimeManager(dummyTimeManager); + emitterEngine.AssignTrackingManager(dummyTrackingManager); prepEngineOrder.Add(emitterEngine); } @@ -456,6 +470,7 @@ namespace Encompass prepEngineOrder, dummyEntityManager, dummyComponentManager, + dummyTrackingManager, dummyMessageManager, dummyComponentUpdateManager, dummyTimeManager, diff --git a/test/EngineTest.cs b/test/EngineTest.cs index a240adf..c1eb422 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -1116,7 +1116,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - foreach (var entity in QueryEntities()) + foreach (var entity in TrackedEntities) { entities.Add(entity); } @@ -1164,7 +1164,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1211,7 +1211,7 @@ namespace Tests { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1278,7 +1278,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1317,7 +1317,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1379,7 +1379,7 @@ namespace Tests { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } } @@ -1435,7 +1435,7 @@ namespace Tests public override void Update(double dt) { entities.Clear(); - entities.AddRange(QueryEntities()); + entities.AddRange(TrackedEntities); } }