From 3599868629727affe5dccb35b7a168ce2f8d11a6 Mon Sep 17 00:00:00 2001 From: sebas77 Date: Mon, 20 May 2019 18:36:07 +0100 Subject: [PATCH] improve EGIDMapper Remove last methods that were still accepting groups as uint improve comments fix EntityCollection bug --- Svelto.Common | 2 +- .../Svelto.ECS.Components.asmdef | 23 ++ .../DataStructures/TypeSafeDictionary.cs | 21 +- Svelto.ECS/EGIDMapper.cs | 16 +- .../EnginesRoot.DoubleBufferedEntityViews.cs | 2 +- .../EnginesRoot.GenericEntityFunctions.cs | 18 +- Svelto.ECS/EntitiesDB.cs | 2 +- Svelto.ECS/EntityCollection.cs | 274 +++++++++--------- Svelto.ECS/EntityStructInitializer.cs | 4 +- Svelto.ECS/ExecuteOnEntitiesDB.cs | 2 +- Svelto.ECS/IEntitiesDB.cs | 187 ++++++++---- Svelto.ECS/IEntityFunctions.cs | 3 - Svelto.ECS/README.md | 50 ++++ Svelto.ECS/Svelto.ECS.asmdef | 15 + 14 files changed, 394 insertions(+), 225 deletions(-) create mode 100644 Svelto.ECS.Components/Svelto.ECS.Components.asmdef create mode 100644 Svelto.ECS/README.md create mode 100644 Svelto.ECS/Svelto.ECS.asmdef diff --git a/Svelto.Common b/Svelto.Common index b63d124..7783bce 160000 --- a/Svelto.Common +++ b/Svelto.Common @@ -1 +1 @@ -Subproject commit b63d124227ed87367b2f0fcdb4a93833a7c9796b +Subproject commit 7783bce4d3465ed7c8e8d2ba5c48881621cf89b8 diff --git a/Svelto.ECS.Components/Svelto.ECS.Components.asmdef b/Svelto.ECS.Components/Svelto.ECS.Components.asmdef new file mode 100644 index 0000000..cdf669d --- /dev/null +++ b/Svelto.ECS.Components/Svelto.ECS.Components.asmdef @@ -0,0 +1,23 @@ +{ + "name": "Svelto.ECS.Components.Unity", + "references": [ + "Unity.Mathematics", + "Svelto.ECS", + "Svelto.Common" + ], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.mathematics", + "expression": "0.0.9", + "define": "UNITY_MATHEMATICS" + } + ] +} \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs index e30b0f2..ce2a8dc 100644 --- a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using Svelto.Common; @@ -36,7 +36,7 @@ namespace Svelto.ECS.Internal { static readonly Type _type = typeof(TValue); static readonly string _typeName = _type.Name; - static readonly bool HasEgid = typeof(INeedEGID).IsAssignableFrom(_type); + static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); public TypeSafeDictionary(uint size) : base(size) { } public TypeSafeDictionary() {} @@ -49,10 +49,11 @@ namespace Svelto.ECS.Internal { try { - if (HasEgid) + if (_hasEgid) { var needEgid = (INeedEGID)tuple.Value; needEgid.ID = new EGID(tuple.Key, groupId); + Add(tuple.Key, (TValue) needEgid); } else @@ -75,7 +76,7 @@ namespace Svelto.ECS.Internal { var typeSafeDictionary = realDic as TypeSafeDictionary; - AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetDirectValue(value.Key), null, + AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null, ref profiler); } } @@ -103,7 +104,7 @@ namespace Svelto.ECS.Internal /// // entity.ID = EGID.UPDATE_REAL_ID_AND_GROUP(entity.ID, toEntityID.groupID, entityCount); - if (HasEgid) + if (_hasEgid) { var needEgid = (INeedEGID)entity; needEgid.ID = toEntityID.Value; @@ -217,18 +218,12 @@ namespace Svelto.ECS.Internal internal ref TValue FindElement(uint entityGidEntityId) { #if DEBUG && !PROFILER - if (FindIndex(entityGidEntityId, out var findIndex) == false) + if (TryFindIndex(entityGidEntityId, out var findIndex) == false) throw new Exception("Entity not found in this group ".FastConcat(typeof(TValue).ToString())); #else - FindIndex(entityGidEntityId, out var findIndex); + TryFindIndex(entityGidEntityId, out var findIndex); #endif return ref _values[findIndex]; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool TryFindElementIndex(uint entityGidEntityId, out uint index) - { - return FindIndex(entityGidEntityId, out index); - } } } \ No newline at end of file diff --git a/Svelto.ECS/EGIDMapper.cs b/Svelto.ECS/EGIDMapper.cs index 3ddfe8e..428ba6f 100644 --- a/Svelto.ECS/EGIDMapper.cs +++ b/Svelto.ECS/EGIDMapper.cs @@ -8,9 +8,21 @@ namespace Svelto.ECS internal TypeSafeDictionary map; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T Entity(EGID id) + public ref T Entity(uint entityID) { - return ref map.FindElement(id.entityID); + return ref map.FindElement(entityID); + } + + public bool TryQueryEntity(uint entityID, out T @value) + { + if (map.TryFindIndex(entityID, out var index)) + { + @value = map.GetDirectValue(index); + return true; + } + + @value = default; + return false; } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs index 79972b4..ee22dde 100644 --- a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs +++ b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs @@ -1,4 +1,4 @@ -using Svelto.DataStructures.Experimental; +using Svelto.DataStructures.Experimental; using EntitiesDB = Svelto.DataStructures.Experimental.FasterDictionary>; diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs index 8fc3ade..111dccc 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs @@ -15,12 +15,6 @@ namespace Svelto.ECS _weakReference = weakReference; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveEntity(uint entityID, uint groupID) where T : IEntityDescriptor, new() - { - RemoveEntity(new EGID(entityID, groupID)); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new() @@ -39,11 +33,6 @@ namespace Svelto.ECS } - public void RemoveEntities(uint groupID) where T : IEntityDescriptor, new() - { - throw new NotImplementedException(); - } - public void RemoveEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new() { @@ -51,16 +40,11 @@ namespace Svelto.ECS } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveGroupAndEntities(uint groupID) + public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) { _weakReference.Target.QueueEntitySubmitOperation( new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(), new EGID(0, groupID))); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) - { - RemoveGroupAndEntities((uint)groupID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Svelto.ECS/EntitiesDB.cs b/Svelto.ECS/EntitiesDB.cs index 737d932..490d25c 100644 --- a/Svelto.ECS/EntitiesDB.cs +++ b/Svelto.ECS/EntitiesDB.cs @@ -194,7 +194,7 @@ namespace Svelto.ECS.Internal if (QueryEntitySafeDictionary(entityGID.groupID, out TypeSafeDictionary safeDictionary) == false) return null; - if (safeDictionary.TryFindElementIndex(entityGID.entityID, out index) == false) + if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) return null; return safeDictionary.GetValuesArray(out _); diff --git a/Svelto.ECS/EntityCollection.cs b/Svelto.ECS/EntityCollection.cs index 2a8ccc8..7cbb550 100644 --- a/Svelto.ECS/EntityCollection.cs +++ b/Svelto.ECS/EntityCollection.cs @@ -13,183 +13,199 @@ namespace Svelto.ECS _count = count; } - public EntityIterator GetEnumerator() { return new EntityIterator(_array, _count); } + public EntityIterator GetEnumerator() + { + return new EntityIterator(_array, _count); + } - readonly T[] _array; + readonly T[] _array; readonly uint _count; - } - public struct EntityCollections where T : struct, IEntityStruct - { - public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() + + public struct EntityIterator : IEnumerator { - _db = db; - _groups = groups; - } + public EntityIterator(T[] array, uint count) : this() + { + _array = array; + _count = count; + _index = -1; + } - public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } + public bool MoveNext() + { + return ++_index < _count; + } - readonly IEntitiesDB _db; - readonly ExclusiveGroup[] _groups; + public void Reset() + { + _index = -1; + } + + public ref T Current => ref _array[_index]; + + T IEnumerator.Current => throw new NotImplementedException(); + object IEnumerator.Current => throw new NotImplementedException(); + + public void Dispose() + { + } + + readonly T[] _array; + readonly uint _count; + int _index; + } } - - public struct EntityCollections where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct - { - public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() - { - _db = db; - _groups = groups; - } - - public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } - - readonly IEntitiesDB _db; - readonly ExclusiveGroup[] _groups; - } - - public struct EntityGroupsIterator : IEnumerator where T : struct, IEntityStruct + + public struct EntityCollections where T : struct, IEntityStruct { - public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() + public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() { _db = db; _groups = groups; - _indexGroup = -1; - _index = -1; } - public bool MoveNext() + public EntityGroupsIterator GetEnumerator() + { + return new EntityGroupsIterator(_db, _groups); + } + + readonly IEntitiesDB _db; + readonly ExclusiveGroup[] _groups; + + public struct EntityGroupsIterator : IEnumerator where T : struct, IEntityStruct { - while (_index + 1 >= _count && ++_indexGroup < _groups.Length) + public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() { + _db = db; + _groups = groups; + _indexGroup = -1; _index = -1; - _array = _db.QueryEntities(_groups[_indexGroup], out _count); } - if (++_index < _count) + public bool MoveNext() { - return true; - } + while (_index + 1 >= _count && ++_indexGroup < _groups.Length) + { + _index = -1; + _array = _db.QueryEntities(_groups[_indexGroup], out _count); + } - return false; - } - - public void Reset() - { - _index = -1; - _indexGroup = -1; - _array = _db.QueryEntities(_groups[0], out _count); - } + return ++_index < _count; + } - public ref T Current => ref _array[_index]; + public void Reset() + { + _index = -1; + _indexGroup = -1; + _array = _db.QueryEntities(_groups[0], out _count); + } - T IEnumerator. Current => throw new NotImplementedException(); - object IEnumerator.Current => throw new NotImplementedException(); - public void Dispose() { } + public ref T Current => ref _array[_index]; - readonly IEntitiesDB _db; - readonly ExclusiveGroup[] _groups; - T[] _array; - uint _count; - int _index; - int _indexGroup; - } + T IEnumerator.Current => throw new NotImplementedException(); + object IEnumerator.Current => throw new NotImplementedException(); - public struct ValueRef - { - readonly T1[] array1; - readonly T2[] array2; + public void Dispose() {} - readonly uint index; + readonly IEntitiesDB _db; + readonly ExclusiveGroup[] _groups; - public ValueRef(T1[] entity1, T2[] entity2, uint i):this() - { - array1 = entity1; - array2 = entity2; - index = i; + T[] _array; + uint _count; + int _index; + int _indexGroup; } - - public ref T1 Item1 => ref array1[index]; - public ref T2 Item2 => ref array2[index]; } - - public struct EntityGroupsIterator : IEnumerator> where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct + + public struct EntityCollections where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct { - public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() + public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this() { _db = db; _groups = groups; - _indexGroup = -1; - _index = -1; } - public bool MoveNext() + public EntityGroupsIterator GetEnumerator() { - while (_index + 1 >= _count && ++_indexGroup < _groups.Length) + return new EntityGroupsIterator(_db, _groups); + } + + readonly IEntitiesDB _db; + readonly ExclusiveGroup[] _groups; + + public struct EntityGroupsIterator : IEnumerator.ValueRef> + where T1 : struct, IEntityStruct + where T2 : struct, IEntityStruct + { + public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this() { + _db = db; + _groups = groups; + _indexGroup = -1; _index = -1; - _value = new ValueRef(_db.QueryEntities(_groups[_indexGroup], out _count), - _db.QueryEntities(_groups[_indexGroup], out var count1), (uint) _index + 1); - -#if DEBUG && !PROFILER - if (_count != count1) - throw new ECSException("number of entities in group doesn't match"); -#endif } - if (++_index < _count) + public bool MoveNext() { - return true; + while (_index + 1 >= _count && ++_indexGroup < _groups.Length) + { + _index = -1; + _array1 = _db.QueryEntities(_groups[_indexGroup], out _count); + _array2 = _db.QueryEntities(_groups[_indexGroup], out var count1); + +#if DEBUG && !PROFILER + if (_count != count1) + throw new ECSException("number of entities in group doesn't match"); +#endif + } + + return ++_index < _count; } - return false; - } - - public void Reset() - { - _index = -1; - _indexGroup = -1; - _value = new ValueRef(_db.QueryEntities(_groups[_indexGroup], out _count), - _db.QueryEntities(_groups[_indexGroup], out var count1), 0); -#if DEBUG && !PROFILER - if (_count != count1) - throw new ECSException("number of entities in group doesn't match"); -#endif - - } + public void Reset() + { + _index = -1; + _indexGroup = -1; + + _array1 = _db.QueryEntities(_groups[0], out _count); + _array2 = _db.QueryEntities(_groups[0], out var count1); +#if DEBUG && !PROFILER + if (_count != count1) + throw new ECSException("number of entities in group doesn't match"); +#endif + } - public ValueRef Current => _value; + public ValueRef Current => new ValueRef(_array1, _array2, (uint) _index); - ValueRef IEnumerator>. Current => throw new NotImplementedException(); - object IEnumerator.Current => throw new NotImplementedException(); - public void Dispose() { } + ValueRef IEnumerator.Current => throw new NotImplementedException(); + object IEnumerator.Current => throw new NotImplementedException(); - readonly IEntitiesDB _db; - readonly ExclusiveGroup[] _groups; - uint _count; - int _index; - int _indexGroup; - ValueRef _value; - } + public void Dispose() {} - public struct EntityIterator : IEnumerator - { - public EntityIterator(T[] array, uint count) : this() - { - _array = array; - _count = count; - _index = -1; - } + readonly IEntitiesDB _db; + readonly ExclusiveGroup[] _groups; + uint _count; + int _index; + int _indexGroup; + T2[] _array2; + T1[] _array1; - public bool MoveNext() { return ++_index < _count; } - public void Reset() { _index = -1; } + public struct ValueRef + { + readonly T1[] array1; + readonly T2[] array2; - public ref T Current => ref _array[_index]; + readonly uint index; - T IEnumerator. Current => throw new NotImplementedException(); - object IEnumerator.Current => throw new NotImplementedException(); - public void Dispose() { } + public ValueRef(T1[] entity1, T2[] entity2, uint i) : this() + { + array1 = entity1; + array2 = entity2; + index = i; + } - readonly T[] _array; - readonly uint _count; - int _index; + public ref T1 Item1 => ref array1[index]; + public ref T2 Item2 => ref array2[index]; + } + } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityStructInitializer.cs b/Svelto.ECS/EntityStructInitializer.cs index 90194e5..0b9e88b 100644 --- a/Svelto.ECS/EntityStructInitializer.cs +++ b/Svelto.ECS/EntityStructInitializer.cs @@ -25,8 +25,8 @@ namespace Svelto.ECS initializer = (T) needEgid; } - if (dictionary.TryFindElementIndex(ID.entityID, out var findElementIndex)) - dictionary.GetValuesArray(out _)[findElementIndex] = initializer; + if (dictionary.TryFindIndex(ID.entityID, out var findElementIndex)) + dictionary.GetDirectValue(findElementIndex) = initializer; } } diff --git a/Svelto.ECS/ExecuteOnEntitiesDB.cs b/Svelto.ECS/ExecuteOnEntitiesDB.cs index 8b7d507..de3a6b0 100644 --- a/Svelto.ECS/ExecuteOnEntitiesDB.cs +++ b/Svelto.ECS/ExecuteOnEntitiesDB.cs @@ -22,7 +22,7 @@ namespace Svelto.ECS.Internal } public void ExecuteOnAllEntities - (ref W value, Action action) + (W value, Action action) where T : struct, IEntityStruct { var type = typeof(T); diff --git a/Svelto.ECS/IEntitiesDB.cs b/Svelto.ECS/IEntitiesDB.cs index 400192d..ca5b7c1 100644 --- a/Svelto.ECS/IEntitiesDB.cs +++ b/Svelto.ECS/IEntitiesDB.cs @@ -4,33 +4,80 @@ namespace Svelto.ECS { public interface IEntitiesDB { + /////////////////////////////////////////////////// + /// Query entities + /// ECS systems are meant to work on a set of Entities. These methods allow to iterate over entity + /// structs inside a given group or an array of groups + /////////////////////////////////////////////////// + /// - /// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using - /// the following functions inside a loop would be a mistake as performance can be significantly impacted - /// return the buffer and the index of the entity inside the buffer using the input EGID + /// Fast and raw return of entities buffer. /// - /// - /// + /// /// /// - bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct; - bool TryQueryEntitiesAndIndex - (uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) + T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) where T : struct, IEntityStruct; - + (T1[], T2[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; + (T1[], T2[], T3[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct; + /// - /// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using - /// the following functions inside a loop would be a mistake as performance can be significantly impacted - /// return the buffer and the index of the entity inside the buffer using the input EGID + /// return entities that can be iterated through the EntityCollection iterator /// - /// - /// + /// /// /// - T[] QueryEntitiesAndIndex(EGID entityGid, out uint index) where T : struct, IEntityStruct; - T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) + EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + where T : struct, IEntityStruct; + + /// + /// return entities found in multiple groups, that can be iterated through the EntityCollection iterator + /// This method is useful to write abstracted engines + /// + /// + /// + /// + EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityStruct; + EntityCollections QueryEntities(ExclusiveGroup[] groups) + where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; + + /////////////////////////////////////////////////// + /// Query entities regardless the group + /// these methods are necessary to create abstracted engines. Engines that can iterate over entities regardless + /// the group + /////////////////////////////////////////////////// + + /// + /// Execute an action on ALL the entities regardless the group. This function doesn't guarantee cache + /// friendliness even if just EntityStructs are used. Safety checks are in place, + /// + /// + /// + /// + void ExecuteOnAllEntities(Action action) + where T : struct, IEntityStruct; + + /// + /// same as above, but can pass some external data to avoid allocations + /// + /// + /// + /// + /// + void ExecuteOnAllEntities(W value, + Action action) where T : struct, IEntityStruct; + /////////////////////////////////////////////////// + /// Query single entities + /// ECS systems are meant to work on a set of Entities. Working on a single entity is sometime necessary, hence + /// the following methods + /// However Because of the double hashing required to identify a specific entity, these function are slower than + /// other query methods when used multiple times! + /////////////////////////////////////////////////// + /// /// QueryUniqueEntity is a contract method that explicitly declare the intention to have just on entity in a /// specific group, usually used for GUI elements @@ -39,9 +86,9 @@ namespace Svelto.ECS /// /// ref T QueryUniqueEntity(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; - + /// - /// + /// return a specific entity by reference. /// /// /// @@ -50,30 +97,44 @@ namespace Svelto.ECS ref T QueryEntity(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct; /// - /// Fast and raw (therefore not safe) return of entities buffer - /// Modifying a buffer would compromise the integrity of the whole DB - /// so they are meant to be used only in performance critical path + /// + ///QueryEntitiesAndIndex is useful to optimize cases when multiple entity structs from the same entity must + /// be queried. This is the use case: + /// + ///ref var ghostPosition = ref entitiesDB.QueryEntitiesAndIndex + /// (MockupRenderingGroups.GhostCubeID, out var index)[index]; + ///ref var ghostScaling = ref entitiesDB.QueryEntities + /// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index]; + ///ref var ghostRotation = ref entitiesDB.QueryEntities + /// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index]; + ///ref var ghostResource = ref entitiesDB.QueryEntities + /// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index]; + /// /// - /// + /// + /// /// /// - T[] QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) + T[] QueryEntitiesAndIndex(EGID entityGid, out uint index) where T : struct, IEntityStruct; + T[] QueryEntitiesAndIndex(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index) where T : struct, IEntityStruct; - (T1[], T2[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) - where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; - (T1[], T2[], T3[]) QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count) - where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct; - EntityCollection QueryEntities(ExclusiveGroup.ExclusiveGroupStruct groupStruct) + /// + /// Like QueryEntitiesAndIndex and only way to get an index only if exists + /// + /// + /// + /// + /// + bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct; + bool TryQueryEntitiesAndIndex + (uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array) where T : struct, IEntityStruct; - - EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityStruct; - EntityCollections QueryEntities(ExclusiveGroup[] groups) - where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct; - + /// - /// this version returns a mapped version of the entity array so that is possible to find the - /// index of the entity inside the returned buffer through it's EGID + /// this method returns a mapped version of the entity array so that is possible to work on multiple entities + /// inside the group through their EGID. This version skip a level of indirection so it's a bit faster than + /// using QueryEntity multiple times (with different EGIDs). /// However mapping can be slow so it must be used for not performance critical paths /// /// @@ -82,24 +143,13 @@ namespace Svelto.ECS /// EGIDMapper QueryMappedEntities(ExclusiveGroup.ExclusiveGroupStruct groupStructId) where T : struct, IEntityStruct; - - /// - /// Execute an action on ALL the entities regardless the group. This function doesn't guarantee cache - /// friendliness even if just EntityStructs are used. - /// Safety checks are in place - /// - /// - /// - /// - void ExecuteOnAllEntities(Action action) - where T : struct, IEntityStruct; - - void ExecuteOnAllEntities(ref W value, - Action action) - where T : struct, IEntityStruct; - + + /////////////////////////////////////////////////// + /// Utility methods + /////////////////////////////////////////////////// + /// - /// + /// check if a specific entity exists /// /// /// @@ -108,7 +158,7 @@ namespace Svelto.ECS bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid); /// - /// + /// know if there is any entity struct in a specific group /// /// /// @@ -116,18 +166,45 @@ namespace Svelto.ECS bool HasAny(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; /// - /// + /// Count the number of entity structs in a specific group /// /// /// /// uint Count(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct; + + /////////////////////////////////////////////////// + /// Publish entities in the entity stream + /// The Entity Stream is a communication mean based on the Publisher/Consumer model. The pattern is + /// 1) changing an entity + /// 2) publish the entity modified + /////////////////////////////////////////////////// /// - /// + /// Publish an Entity Change, through the Entity Stream linked to this database + /// Use case: + /// public void Add(ref ButtonEntityViewStruct buttonEntityViewStruct) +/// { +/// buttonEntityViewStruct.buttonClick.buttonEvent = new DispatchOnSet(buttonEntityViewStruct.ID); +/// +/// buttonEntityViewStruct.buttonClick.buttonEvent.NotifyOnValueSet(_enqueueButtonChange); +/// } +/// +/// public void Remove(ref ButtonEntityViewStruct buttonEntityViewStruct) +/// { +/// buttonEntityViewStruct.buttonClick.buttonEvent.StopNotify(_enqueueButtonChange); +/// } +/// +/// void EnqueueButtonChange(EGID egid, ButtonEvents value) +/// { +/// entitiesDB.QueryEntity(egid) = new ButtonEntityStruct(egid, value); +/// +/// entitiesDB.PublishEntityChange(egid); +/// } /// /// /// void PublishEntityChange(EGID egid) where T : unmanaged, IEntityStruct; + } } \ No newline at end of file diff --git a/Svelto.ECS/IEntityFunctions.cs b/Svelto.ECS/IEntityFunctions.cs index 3c1ee99..49c28ac 100644 --- a/Svelto.ECS/IEntityFunctions.cs +++ b/Svelto.ECS/IEntityFunctions.cs @@ -5,14 +5,11 @@ namespace Svelto.ECS //being entity ID globally not unique, the group must be specified when //an entity is removed. Not specifying the group will attempt to remove //the entity from the special standard group. - void RemoveEntity(uint entityID, uint groupID) where T : IEntityDescriptor, new(); void RemoveEntity(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); void RemoveEntity(EGID entityegid) where T : IEntityDescriptor, new(); - void RemoveEntities(uint groupID) where T : IEntityDescriptor, new(); void RemoveEntities(ExclusiveGroup.ExclusiveGroupStruct groupID) where T : IEntityDescriptor, new(); - void RemoveGroupAndEntities(uint groupID); void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID); void SwapEntityGroup(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID, ExclusiveGroup.ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new(); diff --git a/Svelto.ECS/README.md b/Svelto.ECS/README.md new file mode 100644 index 0000000..ef18d33 --- /dev/null +++ b/Svelto.ECS/README.md @@ -0,0 +1,50 @@ +Svelto Entity Component System for Unity +===================================== + +**Note: The alpha stage of Svelto 2.0 is almost completed, so if you are here to experiment with it, please use the current alpha branch** + +Real Entity-Component-System for c# and Unity (it can be adapted for other c# platforms too). Enables to write encapsulated, uncoupled, highly efficient, data oriented, cache friendly, multi-threaded, code without pain. + +you can find working examples to learn how to use the framework here: + +https://github.com/sebas77/Svelto-ECS-Example (unity) + +https://github.com/sebas77/Svelto.ECS.Vanilla.Example (.net core and standard) + +I advise to clone the example repositories separately from the framework one, both under the same Unity project Assets folder. + +relative article: + +http://www.sebaslab.com/svelto-ecs-2-0-almost-production-ready/ + +http://www.sebaslab.com/ecs-1-0/ + +If you want to know more about the theory and rationale behind this framework: + +http://www.sebaslab.com/ioc-container-for-unity3d-part-1/ + +http://www.sebaslab.com/ioc-container-for-unity3d-part-2/ + +http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-i-dependency-injection/ + +http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-ii-inversion-of-control/ + +http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-iii-entity-component-systems/ + +http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-iv-dependency-inversion-principle/ + +http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-v-drifting-away-from-ioc-containers/ + +new article on optimizations: + +http://www.sebaslab.com/svelto-ecs-svelto-tasks-to-write-data-oriented-cache-friendly-multi-threaded-code-in-unity/ + +Note: if you ever build something with Svelto.ECS that you can share with the community, please do and let me know. Other coders need more examples. + +Copyright (c) Sebastiano Mandalà + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Svelto.ECS/Svelto.ECS.asmdef b/Svelto.ECS/Svelto.ECS.asmdef new file mode 100644 index 0000000..49133e0 --- /dev/null +++ b/Svelto.ECS/Svelto.ECS.asmdef @@ -0,0 +1,15 @@ +{ + "name": "Svelto.ECS", + "references": [ + "Svelto.Common" + ], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [] +} \ No newline at end of file