From 70dbf755099619a731be7bf985f357fcb0469f3d Mon Sep 17 00:00:00 2001 From: James Billy Date: Mon, 8 Jan 2024 14:51:37 +0100 Subject: [PATCH 1/2] Fixes missing component Add/Remove events. --- src/Arch/Core/World.cs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Arch/Core/World.cs b/src/Arch/Core/World.cs index db81f044..5c665780 100644 --- a/src/Arch/Core/World.cs +++ b/src/Arch/Core/World.cs @@ -1,5 +1,6 @@ using System.Diagnostics.Contracts; using System.Threading; +using Arch.Core.Extensions; using Arch.Core.Extensions.Internal; using Arch.Core.Utils; using Collections.Pooled; @@ -297,6 +298,14 @@ public Entity Create(Span types) EntityInfo.Add(entity.Id, recycled.Version, archetype, slot); Size++; OnEntityCreated(entity); + +#if EVENTS + foreach (ref var type in types) + { + OnComponentAdded(entity, type); + } +#endif + return entity; } @@ -343,6 +352,15 @@ internal void Move(Entity entity, Archetype source, Archetype destination, out S [StructuralChange] public void Destroy(Entity entity) { + #if EVENTS + // Raise the OnComponentRemoved event for each component on the entity. + var arch = GetArchetype(entity); + foreach (var compType in arch.Types) + { + OnComponentRemoved(entity, compType); + } + #endif + OnEntityDestroyed(entity); // Remove from archetype @@ -774,6 +792,16 @@ public void Destroy(in QueryDescription queryDescription) foreach (var index in chunk) { var entity = Unsafe.Add(ref entityFirstElement, index); + + #if EVENTS + // Raise the OnComponentRemoved event for each component on the entity. + var arch = GetArchetype(entity); + foreach (var compType in arch.Types) + { + OnComponentRemoved(entity, compType); + } + #endif + OnEntityDestroyed(entity); var version = EntityInfo.GetVersion(entity.Id); From c5cb361a758ef22b724433006e22153f17a618d9 Mon Sep 17 00:00:00 2001 From: James Billy Date: Mon, 8 Jan 2024 14:52:31 +0100 Subject: [PATCH 2/2] Add unit tests for newly added events. --- src/Arch.Tests/EventTest.cs | 82 ++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/Arch.Tests/EventTest.cs b/src/Arch.Tests/EventTest.cs index 62264ce3..d1b5ba01 100644 --- a/src/Arch.Tests/EventTest.cs +++ b/src/Arch.Tests/EventTest.cs @@ -9,7 +9,7 @@ namespace Arch.Tests; /// /// The class -/// adds several methods for checking if events are fired correctly upon entity modifcation. +/// adds several methods for checking if events are fired correctly upon entity modification. /// [TestFixture] public sealed class EventTest @@ -67,6 +67,30 @@ public void AddSingle() _asserter.Clear(); } + [Test] + public void AddSingleFromArchetype() + { + using var world = World.Create(); + world.SubscribeComponentAdded((in Entity entity, ref EventTestComponentOne _) => _asserter.CompOneAdded.Add(entity)); + world.SubscribeComponentAdded((in Entity entity, ref EventTestComponentTwo _) => _asserter.CompTwoAdded.Add(entity)); + + Span archetype = stackalloc ComponentType[] { typeof(EventTestComponentOne) }; + + // Create entity to check if created and add event were fired + var entity = world.Create(archetype); + + _asserter.AssertEvents(compOneAdded: 1); + That(_asserter.CompOneAdded, Does.Contain(entity)); + _asserter.Clear(); + + // Add another component to see if add was fired + world.Add(entity); + + _asserter.AssertEvents(compTwoAdded: 1); + That(_asserter.CompTwoAdded, Does.Contain(entity)); + _asserter.Clear(); + } + [Test] public void AddSingleObject() { @@ -226,6 +250,62 @@ public void RemoveMultiple() _asserter.Clear(); } + [Test] + public void DestroyDoesRaiseComponentRemove() + { + using var world = World.Create(); + world.SubscribeComponentRemoved((in Entity entity,ref EventTestComponentOne _) => _asserter.CompOneRemoved.Add(entity)); + world.SubscribeComponentRemoved((in Entity entity, ref EventTestComponentTwo _) => _asserter.CompTwoRemoved.Add(entity)); + + var entity = world.Create(); + world.Destroy(entity); + + _asserter.AssertEvents(compOneRemoved: 1, compTwoRemoved:1); + That(_asserter.CompOneRemoved, Does.Contain(entity)); + That(_asserter.CompTwoRemoved, Does.Contain(entity)); + _asserter.Clear(); + } + + [Test] + public void DestroyUsingQueryDoesRaiseComponentRemove() + { + using var world = World.Create(); + world.SubscribeComponentRemoved((in Entity entity,ref EventTestComponentOne _) => _asserter.CompOneRemoved.Add(entity)); + world.SubscribeComponentRemoved((in Entity entity, ref EventTestComponentTwo _) => _asserter.CompTwoRemoved.Add(entity)); + + var entity = world.Create(); + + world.Destroy(new QueryDescription().WithExclusive()); + + _asserter.AssertEvents(compOneRemoved: 1, compTwoRemoved:1); + That(_asserter.CompOneRemoved, Does.Contain(entity)); + That(_asserter.CompTwoRemoved, Does.Contain(entity)); + _asserter.Clear(); + } + + [Test] + public void RemoveCompThenDestroyDoesNotRaiseCompRemoveTwice() + { + using var world = World.Create(); + world.SubscribeComponentRemoved((in Entity entity,ref EventTestComponentOne _) => _asserter.CompOneRemoved.Add(entity)); + world.SubscribeComponentRemoved((in Entity entity, ref EventTestComponentTwo _) => _asserter.CompTwoRemoved.Add(entity)); + + var entity = world.Create(); + world.Remove(entity); + + _asserter.AssertEvents(compOneRemoved: 1, compTwoRemoved: 1); + That(_asserter.CompOneRemoved, Does.Contain(entity)); + That(_asserter.CompTwoRemoved, Does.Contain(entity)); + _asserter.Clear(); + + world.Destroy(entity); + + _asserter.AssertEvents(compOneRemoved: 0, compTwoRemoved: 0); + That(_asserter.CompOneRemoved, Does.Not.Contain(entity)); + That(_asserter.CompTwoRemoved, Does.Not.Contain(entity)); + _asserter.Clear(); + } + private class EventAsserter { public readonly List Created = new();