Skip to content

Commit

Permalink
added WhenAdded, WhenChanged and WhenRemoved filter on EntitySetBuild…
Browse files Browse the repository at this point in the history
…er to create reactive EntitySet

added WhenAddedAttribute, WhenChangedAttribute and WhenRemovedAttribute for automatique AEntitySystem EntitySet creation from a World
added Complete method on EntitySet to clear its content if created with a reactive filter
  • Loading branch information
Doraku committed Jun 23, 2019
1 parent 8762b83 commit b65f7fe
Show file tree
Hide file tree
Showing 12 changed files with 390 additions and 20 deletions.
3 changes: 3 additions & 0 deletions source/DefaultEcs.Benchmark/DefaultEcs.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
<Reference Include="Entitas">
<HintPath>..\..\dependency\Entitas.dll</HintPath>
</Reference>
<Reference Include="DesperateDevs.Utils">
<HintPath>..\..\dependency\DesperateDevs.Utils.dll</HintPath>
</Reference>
<!--v2.7.0--><!--
<Reference Include="Svelto.ECS">
<HintPath>..\..\dependency\Svelto.ECS.dll</HintPath>
Expand Down
132 changes: 132 additions & 0 deletions source/DefaultEcs.Test/EntitySetBuilderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,138 @@ public void Build_WithAny_T1_T2_Should_return_EntitySet_with_all_Entity_with_com
}
}

[Fact]
public void Build_WhenAdded_T_Should_return_EntitySet_with_all_Entity_when_component_T_is_added()
{
using (World world = new World(4))
using (EntitySet set = world.GetEntities().WhenAdded<bool>().Build())
{
List<Entity> entities = new List<Entity>
{
world.CreateEntity(),
world.CreateEntity(),
world.CreateEntity(),
world.CreateEntity()
};

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Set(true);
}

Check.That(set.GetEntities().ToArray()).ContainsExactly(entities);

set.Complete();

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Set(false);
}

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Disable<bool>();
}

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Enable<bool>();
}

Check.That(set.GetEntities().ToArray()).ContainsExactly(entities);
}
}

[Fact]
public void Build_WhenChanged_T_Should_return_EntitySet_with_all_Entity_when_component_T_is_added_and_changed()
{
using (World world = new World(4))
using (EntitySet set = world.GetEntities().WhenChanged<bool>().Build())
{
List<Entity> entities = new List<Entity>
{
world.CreateEntity(),
world.CreateEntity(),
world.CreateEntity(),
world.CreateEntity()
};

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Set(true);
}

Check.That(set.Count).IsZero();

set.Complete();

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Set(false);
}

Check.That(set.GetEntities().ToArray()).ContainsExactly(entities);
}
}

[Fact]
public void Build_WhenRemoved_T_Should_return_EntitySet_with_all_Entity_when_component_T_is_removed()
{
using (World world = new World(4))
using (EntitySet set = world.GetEntities().WhenRemoved<bool>().Build())
{
List<Entity> entities = new List<Entity>
{
world.CreateEntity(),
world.CreateEntity(),
world.CreateEntity(),
world.CreateEntity()
};

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Set(true);
}

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Disable<bool>();
}

Check.That(set.GetEntities().ToArray()).ContainsExactly(entities);

foreach (Entity entity in entities)
{
entity.Enable<bool>();
}

Check.That(set.Count).IsZero();

foreach (Entity entity in entities)
{
entity.Remove<bool>();
}

Check.That(set.GetEntities().ToArray()).ContainsExactly(entities);
}
}

#endregion
}
}
3 changes: 3 additions & 0 deletions source/DefaultEcs/DefaultEcs.Package.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ added MaxCapacity, Capacity and Size properties on EntityCommandRecorder
added EntityAdded and EntityRemoved events on EntitySet
added EntityAdded and EntityRemoved events on AEntitySystem
added EntityDisposed event on World
added WhenAdded, WhenChanged and WhenRemoved filter on EntitySetBuilder to create reactive EntitySet
added WhenAddedAttribute, WhenChangedAttribute and WhenRemovedAttribute for automatique AEntitySystem EntitySet creation from a World
added Complete method on EntitySet to clear its content if created with a reactive filter

breaking changes
removed IEntitySetObserver and implementation, use EntityAdded and EntityRemoved events on EntitySet instead
Expand Down
33 changes: 25 additions & 8 deletions source/DefaultEcs/EntitySet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public sealed class EntitySet : IDisposable
private static readonly MethodInfo _componentsDoNotContains;
private static readonly Dictionary<string, Predicate<ComponentEnum>> _filters;

private readonly bool _needClearing;
private readonly int _worldId;
private readonly int _maxEntityCount;
private readonly Predicate<ComponentEnum> _filter;
Expand Down Expand Up @@ -83,12 +84,14 @@ static EntitySet()
}

internal EntitySet(
bool needClearing,
World world,
ComponentEnum withFilter,
ComponentEnum withoutFilter,
List<ComponentEnum> withAnyFilters,
List<Func<EntitySet, World, IDisposable>> subscriptions)
{
_needClearing = needClearing;
_worldId = world.WorldId;
_maxEntityCount = world.MaxEntityCount;

Expand All @@ -104,11 +107,14 @@ internal EntitySet(
_entities = EmptyArray<Entity>.Value;
Count = 0;

for (int i = 0; i <= Math.Min(world.Info.EntityInfos.Length, world.LastEntityId); ++i)
if (!_needClearing)
{
if (_filter(world.Info.EntityInfos[i].Components))
for (int i = 0; i <= Math.Min(world.Info.EntityInfos.Length, world.LastEntityId); ++i)
{
Add(i);
if (_filter(world.Info.EntityInfos[i].Components))
{
Add(i);
}
}
}
}
Expand All @@ -132,12 +138,9 @@ private static Predicate<ComponentEnum> GetFilter(ComponentEnum withFilter, Comp
{
filterEx = Expression.And(filterEx, Expression.Call(components, _componentsDoNotContains, Expression.Constant(withoutFilter.Copy())));
}
if (withAnyFilters != null)
foreach (ComponentEnum f in withAnyFilters ?? Enumerable.Empty<ComponentEnum>())
{
foreach (ComponentEnum f in withAnyFilters)
{
filterEx = Expression.And(filterEx, Expression.Not(Expression.Call(components, _componentsDoNotContains, Expression.Constant(f.Copy()))));
}
filterEx = Expression.And(filterEx, Expression.Not(Expression.Call(components, _componentsDoNotContains, Expression.Constant(f.Copy()))));
}
filter = Expression.Lambda<Predicate<ComponentEnum>>(filterEx, components).Compile();

Expand Down Expand Up @@ -249,6 +252,20 @@ internal void CheckedRemove<T>(in ComponentRemovedMessage<T> message)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<Entity> GetEntities() => GetEntities(0, Count);

/// <summary>
/// Clears current instance of its entities if it was created with some reactive filter (<seealso cref="EntitySetBuilder.WhenAdded{T}"/>, <see cref="EntitySetBuilder.WhenChanged{T}"/> or <see cref="EntitySetBuilder.WhenRemoved{T}"/>).
/// Does nothing if it was created from a static filter.
/// This method need to be called after current instance content has been processed in a update cycle.
/// </summary>
public void Complete()
{
if (_needClearing && Count > 0)
{
Count = 0;
_mapping.Fill(-1);
}
}

#endregion

#region IDisposable
Expand Down
Loading

0 comments on commit b65f7fe

Please sign in to comment.