示例#1
0
        public LocalFasterReadOnlyList <ExclusiveGroupStruct> FindGroups <T1>() where T1 : IBaseEntityComponent
        {
            FasterList <ExclusiveGroupStruct> result = localgroups.Value.groupArray;

            result.FastClear();
            if (groupsPerComponent.TryGetValue(TypeRefWrapper <T1> .wrapper
                                               , out FasterDictionary <ExclusiveGroupStruct, ITypeSafeDictionary> result1)
                == false)
            {
                return(result);
            }

            var result1Count           = result1.count;
            var fasterDictionaryNodes1 = result1.unsafeKeys;

            for (int j = 0; j < result1Count; j++)
            {
                var group = fasterDictionaryNodes1[j].key;
                if (group.IsEnabled())
                {
                    result.Add(group);
                }
            }

            return(result);
        }
示例#2
0
        public FasterList <ExclusiveGroupStruct> Evaluate()
        {
            groups.FastClear();

            foreach (var item in sets)
            {
                groups.Add(item);
            }

            return(groups);
        }
        /// <summary>
        /// Remember that this operation os O(N*M*P) where N,M,P are the number of groups where each component
        /// is found.
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <returns></returns>
        public LocalFasterReadOnlyList <ExclusiveGroupStruct> FindGroups <T1, T2, T3>()
            where T1 : IEntityComponent where T2 : IEntityComponent where T3 : IEntityComponent
        {
            FasterList <ExclusiveGroupStruct> result = groups.Value;

            result.FastClear();
            if (groupsPerEntity.TryGetValue(TypeRefWrapper <T1> .wrapper
                                            , out FasterDictionary <uint, ITypeSafeDictionary> result1) == false)
            {
                return(result);
            }
            if (groupsPerEntity.TryGetValue(TypeRefWrapper <T2> .wrapper
                                            , out FasterDictionary <uint, ITypeSafeDictionary> result2) == false)
            {
                return(result);
            }
            if (groupsPerEntity.TryGetValue(TypeRefWrapper <T3> .wrapper
                                            , out FasterDictionary <uint, ITypeSafeDictionary> result3) == false)
            {
                return(result);
            }

            var result1Count           = result1.count;
            var result2Count           = result2.count;
            var result3Count           = result3.count;
            var fasterDictionaryNodes1 = result1.unsafeKeys;
            var fasterDictionaryNodes2 = result2.unsafeKeys;
            var fasterDictionaryNodes3 = result3.unsafeKeys;

            for (int i = 0; i < result1Count; i++)
            {
                var key = fasterDictionaryNodes1[i].key;

                for (int j = 0; j < result2Count; ++j)
                {
                    if (key == fasterDictionaryNodes2[j].key)
                    {
                        for (int k = 0; k < result3Count; ++k)
                        {
                            if (key == fasterDictionaryNodes3[k].key)
                            {
                                result.Add(new ExclusiveGroupStruct(key));
                                break;
                            }
                        }

                        break;
                    }
                }
            }

            return(result);
        }
示例#4
0
        IEnumerator CheckIfDead()
        {
            var enemyIterationInfo = new FasterList <EGID>();

            while (true)
            {
                //wait for enemies to be created
                while (entitiesDB.HasAny <EnemyEntityStruct>(ECSGroups.ActiveEnemies) == false)
                {
                    yield return(null);
                }

                //Very important: Since I use groups, I know that the entity queries are relative to the enemy
                //entities and therefore the count value will be always the same. Choose your groups wisely.
                //if entities from two entitiy descriptors share one entity view and are created in the same group,
                //the total number of that specific entity views queried will be NumberOfEntityA + NumberOfEntityB.
                //Groups assure that the number of entity view are the same in the same group, even if the entity view
                //is shared with other entities in other groups.
                int count;
                var enemyEntitiesViews  = entitiesDB.QueryEntities <EnemyEntityViewStruct>(ECSGroups.ActiveEnemies, out count);
                var enemyEntitiesHealth = entitiesDB.QueryEntities <HealthEntityStruct>(ECSGroups.ActiveEnemies, out count);

                for (int i = 0; i < count; i++)
                {
                    if (enemyEntitiesHealth[i].dead == false)
                    {
                        continue;
                    }

                    SetParametersForDeath(ref enemyEntitiesViews[i]);

                    enemyIterationInfo.Add(enemyEntitiesHealth[i].ID);
                }

                for (int i = 0; i < enemyIterationInfo.Count; i++)
                {
                    //don't remove, but swap. This is how pooling is done in Svelto.
                    //Pooling is not needed when just pure EntityStructs are generated as they are allocation free.
                    //The swap is necessary so that the enemy entity cannot be shot while it's dying
                    //the group works like a sort of state for the Enemy entity in this case.
                    var newID = _entityFunctions.SwapEntityGroup <EnemyEntityDescriptor>(enemyIterationInfo[i],
                                                                                         (int)ECSGroups
                                                                                         .DeadEnemiesGroups);

                    _enemyDeadSequencer.Next(this, EnemyDeathCondition.Death, newID);
                }

                enemyIterationInfo.FastClear();

                yield return(null);
            }
        }
示例#5
0
        public LocalFasterReadOnlyList <ExclusiveGroupStruct> FindGroups <T1, T2>()
            where T1 : IBaseEntityComponent where T2 : IBaseEntityComponent
        {
            FasterList <ExclusiveGroupStruct> result = localgroups.Value.groupArray;

            result.FastClear();
            if (groupsPerComponent.TryGetValue(TypeRefWrapper <T1> .wrapper
                                               , out FasterDictionary <ExclusiveGroupStruct, ITypeSafeDictionary> result1)
                == false)
            {
                return(result);
            }
            if (groupsPerComponent.TryGetValue(TypeRefWrapper <T2> .wrapper
                                               , out FasterDictionary <ExclusiveGroupStruct, ITypeSafeDictionary> result2)
                == false)
            {
                return(result);
            }

            var result1Count           = result1.count;
            var result2Count           = result2.count;
            var fasterDictionaryNodes1 = result1.unsafeKeys;
            var fasterDictionaryNodes2 = result2.unsafeKeys;

            for (int i = 0; i < result1Count; i++)
            {
                var groupID = fasterDictionaryNodes1[i].key;
                if (!groupID.IsEnabled())
                {
                    continue;
                }

                for (int j = 0; j < result2Count; j++)
                {
                    //if the same group is found used with both T1 and T2
                    if (groupID == fasterDictionaryNodes2[j].key)
                    {
                        result.Add(groupID);
                        break;
                    }
                }
            }

            return(result);
        }
        public LocalFasterReadOnlyList <ExclusiveGroupStruct> FindGroups <T1>() where T1 : IEntityComponent
        {
            FasterList <ExclusiveGroupStruct> result = groups.Value;

            result.FastClear();
            if (_groupsPerEntity.TryGetValue(TypeRefWrapper <T1> .wrapper
                                             , out FasterDictionary <uint, ITypeSafeDictionary> result1) == false)
            {
                return(result);
            }

            var result1Count           = result1.count;
            var fasterDictionaryNodes1 = result1.unsafeKeys;

            for (int j = 0; j < result1Count; j++)
            {
                result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[j].key));
            }

            return(result);
        }
        public LocalFasterReadOnlyList <ExclusiveGroupStruct> FindGroups <T1, T2>() where T1 : IEntityComponent  where T2 : IEntityComponent
        {
            FasterList <ExclusiveGroupStruct> result = groups.Value;

            result.FastClear();
            if (_groupsPerEntity.TryGetValue(TypeRefWrapper <T1> .wrapper
                                             , out FasterDictionary <uint, ITypeSafeDictionary> result1) == false)
            {
                return(result);
            }
            if (_groupsPerEntity.TryGetValue(TypeRefWrapper <T2> .wrapper
                                             , out FasterDictionary <uint, ITypeSafeDictionary> result2) == false)
            {
                return(result);
            }

            var result1Count           = result1.count;
            var result2Count           = result2.count;
            var fasterDictionaryNodes1 = result1.unsafeKeys;
            var fasterDictionaryNodes2 = result2.unsafeKeys;

            for (int i = 0; i < result1Count; i++)
            {
                for (int j = 0; j < result2Count; j++)
                {
                    //if the same group is found used with both T1 and T2
                    if (fasterDictionaryNodes1[i].key == fasterDictionaryNodes2[j].key)
                    {
                        result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[i].key));
                        break;
                    }
                }
            }

            return(result);
        }
        /// <summary>
        /// Todo: it would be probably better to split even further the logic between submission and callbacks
        /// Something to do when I will optimize the callbacks
        /// </summary>
        /// <param name="profiler"></param>
        /// <param name="maxNumberOfOperations"></param>
        IEnumerator SingleSubmission(PlatformProfiler profiler, uint maxNumberOfOperations)
        {
#if UNITY_NATIVE
            NativeOperationSubmission(profiler);
#endif
            ClearChecks();

            bool entitiesAreSubmitted = false;
            uint numberOfOperations   = 0;

            if (_entitiesOperations.count > 0)
            {
                using (profiler.Sample("Remove and Swap operations"))
                {
                    _transientEntitiesOperations.FastClear();
                    _entitiesOperations.CopyValuesTo(_transientEntitiesOperations);
                    _entitiesOperations.FastClear();

                    EntitySubmitOperation[] entitiesOperations = _transientEntitiesOperations.ToArrayFast(out var count);
                    for (var i = 0; i < count; i++)
                    {
                        try
                        {
                            switch (entitiesOperations[i].type)
                            {
                            case EntitySubmitOperationType.Swap:
                                MoveEntityFromAndToEngines(entitiesOperations[i].builders,
                                                           entitiesOperations[i].fromID, entitiesOperations[i].toID);
                                break;

                            case EntitySubmitOperationType.Remove:
                                MoveEntityFromAndToEngines(entitiesOperations[i].builders,
                                                           entitiesOperations[i].fromID, null);
                                break;

                            case EntitySubmitOperationType.RemoveGroup:
                                RemoveEntitiesFromGroup(
                                    entitiesOperations[i].fromID.groupID, profiler);
                                break;

                            case EntitySubmitOperationType.SwapGroup:
                                SwapEntitiesBetweenGroups(entitiesOperations[i].fromID.groupID,
                                                          entitiesOperations[i].toID.groupID, profiler);
                                break;
                            }
                        }
                        catch
                        {
                            var str = "Crash while executing Entity Operation "
                                      .FastConcat(entitiesOperations[i].type.ToString());


                            Svelto.Console.LogError(str.FastConcat(" ")
#if DEBUG && !PROFILE_SVELTO
                                                    .FastConcat(entitiesOperations[i].trace.ToString())
#endif
                                                    );

                            throw;
                        }

                        ++numberOfOperations;

                        if ((uint)numberOfOperations >= (uint)maxNumberOfOperations)
                        {
                            yield return(null);

                            numberOfOperations = 0;
                        }
                    }
                }

                entitiesAreSubmitted = true;
            }

            _groupedEntityToAdd.Swap();

            if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.count > 0)
            {
                using (profiler.Sample("Add operations"))
                {
                    try
                    {
                        using (profiler.Sample("Add entities to database"))
                        {
                            //each group is indexed by entity view type. for each type there is a dictionary indexed by entityID
                            foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup)
                            {
                                var groupID = groupToSubmit.Key;
                                var groupDB = GetOrCreateGroup(groupID, profiler);

                                //add the entityComponents in the group
                                foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID])
                                {
                                    var type = entityComponentsToSubmit.Key;
                                    var targetTypeSafeDictionary = entityComponentsToSubmit.Value;
                                    var wrapper = new RefWrapperType(type);

                                    ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper,
                                                                                              targetTypeSafeDictionary);

                                    //Fill the DB with the entity components generate this frame.
                                    dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID);
                                }
                            }
                        }

                        //then submit everything in the engines, so that the DB is up to date with all the entity components
                        //created by the entity built
                        using (profiler.Sample("Add entities to engines"))
                        {
                            foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup)
                            {
                                var groupID = groupToSubmit.Key;
                                var groupDB = _groupEntityComponentsDB[groupID];
//entityComponentsToSubmit is the array of components found in the groupID per component type.
//if there are N entities to submit, and M components type to add for each entity, this foreach will run NxM times.
                                foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID])
                                {
                                    var realDic = groupDB[new RefWrapperType(entityComponentsToSubmit.Key)];

                                    entityComponentsToSubmit.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesAddRemove, realDic,
                                                                                                    null, new ExclusiveGroupStruct(groupID), in profiler);

                                    numberOfOperations += entityComponentsToSubmit.Value.count;

                                    if (numberOfOperations >= maxNumberOfOperations)
                                    {
                                        yield return(null);

                                        numberOfOperations = 0;
                                    }
                                }
                            }
                        }
                    }
                    finally
                    {
                        using (profiler.Sample("clear double buffering"))
                        {
                            //other can be cleared now, but let's avoid deleting the dictionary every time
                            _groupedEntityToAdd.ClearOther();
                        }
                    }
                }

                entitiesAreSubmitted = true;
            }

            if (entitiesAreSubmitted)
            {
                var enginesCount = _reactiveEnginesSubmission.count;
                for (int i = 0; i < enginesCount; i++)
                {
                    _reactiveEnginesSubmission[i].EntitiesSubmitted();
                }
            }
        }
示例#9
0
        void SubmitEntityViews()
        {
            using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission"))
            {
                if (_entitiesOperations.Count > 0)
                {
                    using (profiler.Sample("Remove and Swap operations"))
                    {
                        _transientEntitiesOperations.FastClear();
                        var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count);
                        _transientEntitiesOperations.AddRange(entitySubmitOperations, count);
                        _entitiesOperations.FastClear();

                        var entitiesOperations = _transientEntitiesOperations.ToArrayFast();
                        for (var i = 0; i < _transientEntitiesOperations.Count; i++)
                        {
                            try
                            {
                                switch (entitiesOperations[i].type)
                                {
                                case EntitySubmitOperationType.Swap:
                                    SwapEntityGroup(entitiesOperations[i].builders,
                                                    entitiesOperations[i].entityDescriptor,
                                                    entitiesOperations[i].fromID,
                                                    entitiesOperations[i].toID);
                                    break;

                                case EntitySubmitOperationType.Remove:
                                    MoveEntity(entitiesOperations[i].builders,
                                               entitiesOperations[i].fromID,
                                               entitiesOperations[i].entityDescriptor, null);
                                    break;

                                case EntitySubmitOperationType.RemoveGroup:
                                    if (entitiesOperations[i].entityDescriptor == null)
                                    {
                                        RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID);
                                    }
                                    else
                                    {
                                        RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID,
                                                                     entitiesOperations[i].entityDescriptor);
                                    }

                                    break;
                                }
                            }
                            catch (Exception e)
                            {
                                var str = "Crash while executing Entity Operation "
                                          .FastConcat(entitiesOperations[i].type.ToString());
#if RELAXED_ECS && !PROFILER
                                Console.LogException(str.FastConcat(" "
#if DEBUG && !PROFILER
                                                                    , entitiesOperations[i].trace
#endif
                                                                    ), e);
#else
                                throw new ECSException(str.FastConcat(" ")
#if DEBUG && !PROFILER
                                                       .FastConcat(entitiesOperations[i].trace)
#endif
                                                       , e);
#endif
                            }
                        }
                    }
                }

                if (_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0)
                {
                    using (profiler.Sample("Add operations"))
                    {
                        //use other as source from now on current will be use to write new entityViews
                        _groupedEntityToAdd.Swap();

                        try
                        {
                            //Note: if N entity of the same type are added on the same frame the Add callback is called
                            //N times on the same frame. if the Add callback builds a new entity, that entity will not
                            //be available in the database until the N callbacks are done. Solving this could be
                            //complicated as callback and database update must be interleaved.
                            AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd, profiler);
                        }
                        finally
                        {
                            //other can be cleared now, but let's avoid deleting the dictionary every time
                            _groupedEntityToAdd.ClearOther();
                        }
                    }
                }
            }
        }
示例#10
0
        /// <summary>
        /// Remember that this operation os O(N*M*P) where N,M,P are the number of groups where each component
        /// is found.
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="T3"></typeparam>
        /// <returns></returns>
        public LocalFasterReadOnlyList <ExclusiveGroupStruct> FindGroups <T1, T2, T3>()
            where T1 : IEntityComponent where T2 : IEntityComponent where T3 : IEntityComponent
        {
            FasterList <ExclusiveGroupStruct> result = groups.Value;

            result.FastClear();
            if (groupsPerEntity.TryGetValue(TypeRefWrapper <T1> .wrapper
                                            , out FasterDictionary <ExclusiveGroupStruct, ITypeSafeDictionary>
                                            groupOfEntities1) == false)
            {
                return(result);
            }
            if (groupsPerEntity.TryGetValue(TypeRefWrapper <T2> .wrapper
                                            , out FasterDictionary <ExclusiveGroupStruct, ITypeSafeDictionary>
                                            groupOfEntities2) == false)
            {
                return(result);
            }
            if (groupsPerEntity.TryGetValue(TypeRefWrapper <T3> .wrapper
                                            , out FasterDictionary <ExclusiveGroupStruct, ITypeSafeDictionary>
                                            groupOfEntities3) == false)
            {
                return(result);
            }

            var result1Count           = groupOfEntities1.count;
            var result2Count           = groupOfEntities2.count;
            var result3Count           = groupOfEntities3.count;
            var fasterDictionaryNodes1 = groupOfEntities1.unsafeKeys;
            var fasterDictionaryNodes2 = groupOfEntities2.unsafeKeys;
            var fasterDictionaryNodes3 = groupOfEntities3.unsafeKeys;

            //
            //TODO: I have to find once for ever a solution to be sure that the entities in the groups match
            //Currently this returns group where the entities are found, but the entities may not match in these
            //groups.
            //Checking the size of the entities is an early check, needed, but not sufficient, as entities components may
            //coincidentally match in number but not from which entities they are generated

            //foreach group where T1 is found
            for (int i = 0; i < result1Count; i++)
            {
                var groupT1 = fasterDictionaryNodes1[i].key;

                //foreach group where T2 is found
                for (int j = 0; j < result2Count; ++j)
                {
                    if (groupT1 == fasterDictionaryNodes2[j].key)
                    {
                        //foreach group where T3 is found
                        for (int k = 0; k < result3Count; ++k)
                        {
                            if (groupT1 == fasterDictionaryNodes3[k].key)
                            {
                                result.Add(new ExclusiveGroupStruct(groupT1));
                                break;
                            }
                        }

                        break;
                    }
                }
            }

            return(result);
        }