///--------------------------------------------
        ///
        void MoveEntity(IEntityBuilder[] entityBuilders, EGID entityGID, Type originalDescriptorType, EGID toEntityGID,
                        Dictionary <Type, ITypeSafeDictionary> toGroup = null)
        {
            var profiler = new PlatformProfiler();

            using (profiler.StartNewSession("Move Entity"))
            {
                //for each entity view generated by the entity descriptor
                Dictionary <Type, ITypeSafeDictionary> fromGroup;

                if (_groupEntityDB.TryGetValue(entityGID.groupID, out fromGroup) == false)
                {
                    throw new ECSException("from group not found eid: "
                                           .FastConcat(entityGID.entityID).FastConcat(" group: ")
                                           .FastConcat(entityGID.groupID));
                }

                ITypeSafeDictionary entityInfoViewDic;
                EntityInfoView      entityInfoView = default;

                //Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor!
                bool correctEntityDescriptorFound = true;
                if (fromGroup.TryGetValue(_entityInfoView, out entityInfoViewDic) &&
                    (entityInfoViewDic as TypeSafeDictionary <EntityInfoView>).TryGetValue
                        (entityGID.entityID, out entityInfoView) &&
                    (correctEntityDescriptorFound = entityInfoView.type == originalDescriptorType))
                {
                    var entitiesToMove = entityInfoView.entitiesToBuild;

                    for (int i = 0; i < entitiesToMove.Length; i++)
                    {
                        MoveEntityView(entityGID, toEntityGID, toGroup, fromGroup, entitiesToMove[i].GetEntityType(), profiler);
                    }
                }
                //otherwise it's a normal static entity descriptor
                else
                {
#if DEBUG && !PROFILER
                    if (correctEntityDescriptorFound == false)
#if RELAXED_ECS
                    { Console.LogError(INVALID_DYNAMIC_DESCRIPTOR_ERROR
                                       .FastConcat(" ID ").FastConcat(entityGID.entityID)
                                       .FastConcat(" group ID ").FastConcat(entityGID.groupID).FastConcat(
                                           " descriptor found: ", entityInfoView.type.Name,
                                           " descriptor Excepted ", originalDescriptorType.Name)); }
#else
                    { throw new ECSException(INVALID_DYNAMIC_DESCRIPTOR_ERROR.FastConcat(" ID ").FastConcat(entityGID.entityID)
                                             .FastConcat(" group ID ").FastConcat(entityGID.groupID).FastConcat(
                                                 " descriptor found: ", entityInfoView.type.Name, " descriptor Excepted ",
                                                 originalDescriptorType.Name)); }
#endif
#endif

                    for (var i = 0; i < entityBuilders.Length; i++)
                    {
                        MoveEntityView(entityGID, toEntityGID, toGroup, fromGroup, entityBuilders[i].GetEntityType(), profiler);
                    }
                }
            }
        }
        void RemoveGroupAndEntitiesFromDB(uint groupID)
        {
            var profiler = new PlatformProfiler();

            using (profiler.StartNewSession("Remove Group"))
            {
                var dictionariesOfEntities = _groupEntityDB[groupID];
                foreach (var dictionaryOfEntities in dictionariesOfEntities)
                {
                    var platformProfiler = profiler;
                    dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_entityEngines, ref platformProfiler);
                    var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key];
                    groupedGroupOfEntities.Remove(groupID);
                }

                //careful, in this case I assume you really don't want to use this group anymore
                //so I remove it from the database
                _groupEntityDB.Remove(groupID);
            }
        }
        /// <summary>
        /// Dispose an EngineRoot once not used anymore, so that all the
        /// engines are notified with the entities removed.
        /// It's a clean up process.
        /// </summary>
        public void Dispose()
        {
            var profiler = new PlatformProfiler();

            using (profiler.StartNewSession("Final Dispose"))
            {
                foreach (var groups in _groupEntityDB)
                {
                    foreach (var entityList in groups.Value)
                    {
                        try
                        {
                            entityList.Value.RemoveEntitiesFromEngines(_entityEngines, ref profiler);
                        }
                        catch (Exception e)
                        {
                            Console.LogException(e);
                        }
                    }
                }

                foreach (var engine in _disposableEngines)
                {
                    try
                    {
                        engine.Dispose();
                    }
                    catch (Exception e)
                    {
                        Console.LogException(e);
                    }
                }
            }

            GC.SuppressFinalize(this);
        }
        void SubmitEntityViews()
        {
            var profiler = new PlatformProfiler();

            using (profiler.StartNewSession("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, (int)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
                                Console.LogException(str.FastConcat(" ", entitiesOperations[i].trace), e);
#else
                                throw new ECSException(str.FastConcat(" ")
#if DEBUG && !PROFILER
                                                       .FastConcat(entitiesOperations[i].trace)
#endif
                                                       , e);
#endif
                            }
                        }
                    }
                }

                if (_groupedEntityToAdd.entitiesCreatedPerGroup.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();
                        }
                    }
                }
            }
        }
Exemple #5
0
        IEnumerator IntervaledTick()
        {
//this is of fundamental importance: Never create implementors as Monobehaviour just to hold
//data (especially if read only data). Data should always been retrieved through a service layer
//regardless the data source.
//The benefit are numerous, including the fact that changing data source would require
//only changing the service code. In this simple example I am not using a Service Layer
//but you can see the point.
//Also note that I am loading the data only once per application run, outside the
//main loop. You can always exploit this trick when you now that the data you need
//to use will never change
            var enemiestoSpawn  = ReadEnemySpawningDataServiceRequest();
            var enemyAttackData = ReadEnemyAttackDataServiceRequest();

            float[] spawningTimes = new float[enemiestoSpawn.Length];

            for (int i = enemiestoSpawn.Length - 1; i >= 0 && _numberOfEnemyToSpawn > 0; --i)
            {
                spawningTimes[i] = enemiestoSpawn[i].enemySpawnData.spawnTime;
            }

            _enemyFactory.Preallocate();

            while (true)
            {
//Svelto.Tasks can yield Unity YieldInstructions but this comes with a performance hit
//so the fastest solution is always to use custom enumerators. To be honest the hit is minimal
//but it's better to not abuse it.
                yield return(_waitForSecondsEnumerator);

                var profiler = new PlatformProfiler();
                using (profiler.StartNewSession("EnemySpawning"))
                {
                    //cycle around the enemies to spawn and check if it can be spawned
                    for (int i = enemiestoSpawn.Length - 1; i >= 0 && _numberOfEnemyToSpawn > 0; --i)
                    {
                        if (spawningTimes[i] <= 0.0f)
                        {
                            var spawnData = enemiestoSpawn[i];

                            //In this example every kind of enemy generates the same list of EntityViews
                            //therefore I always use the same EntityDescriptor. However if the
                            //different enemies had to create different EntityViews for different
                            //engines, this would have been a good example where EntityDescriptorHolder
                            //could have been used to exploit the the kind of polymorphism explained
                            //in my articles.
                            EnemyAttackStruct enemyAttackStruct = new EnemyAttackStruct
                            {
                                attackDamage      = enemyAttackData[i].enemyAttackData.attackDamage,
                                timeBetweenAttack = enemyAttackData[i].enemyAttackData.timeBetweenAttacks
                            };

                            //has got a compatible entity previously disabled and can be reused?
                            //Note, pooling make sense only for Entities that use implementors.
                            //A pure struct based entity doesn't need pooling because it never allocates.
                            //to simplify the logic, we use a recycle group for each entity type
                            var fromGroupId = (int)ECSGroups.EnemiesToRecycleGroups +
                                              (int)spawnData.enemySpawnData.targetType;

                            if (entitiesDB.HasAny <EnemyEntityViewStruct>(fromGroupId))
                            {
                                using (profiler.Sample("Recycling"))
                                {
                                    ReuseEnemy(fromGroupId, spawnData);
                                }
                            }
                            else
                            {
                                using (profiler.Sample("Building"))
                                {
                                    _enemyFactory.Build(spawnData.enemySpawnData, ref enemyAttackStruct);
                                }
                            }

                            spawningTimes[i] = spawnData.enemySpawnData.spawnTime;
                            _numberOfEnemyToSpawn--;
                        }

                        spawningTimes[i] -= SECONDS_BETWEEN_SPAWNS;
                    }
                }
            }
        }
Exemple #6
0
        ///--------------------------------------------
        ///
        void MoveEntity(IEntityBuilder[] entityBuilders, EGID fromEntityGID, Type originalDescriptorType, EGID?toEntityGID)
        {
            var profiler = new PlatformProfiler();

            using (profiler.StartNewSession("Move Entity"))
            {
                //for each entity view generated by the entity descriptor
                if (_groupEntityDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false)
                {
                    throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID)
                                           .FastConcat(" group: ").FastConcat(fromEntityGID.groupID));
                }

                //Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor!
                bool correctEntityDescriptorFound = true;

                EntityStructInfoView entityInfoView = default;
                if (fromGroup.TryGetValue(ENTITY_INFO_VIEW_TYPE, out var entityInfoViewDic) &&
                    (entityInfoViewDic as TypeSafeDictionary <EntityStructInfoView>).TryGetValue(
                        fromEntityGID.entityID, out entityInfoView) && (correctEntityDescriptorFound =
                                                                            entityInfoView.type == originalDescriptorType))
                {
                    var entitiesToMove = entityInfoView.entitiesToBuild;

                    Dictionary <Type, ITypeSafeDictionary> toGroup = null;

                    if (toEntityGID != null)
                    {
                        var toGroupID = toEntityGID.Value.groupID;

                        if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false)
                        {
                            toGroup = _groupEntityDB[toGroupID] = new Dictionary <Type, ITypeSafeDictionary>();
                        }
                    }

                    for (int i = 0; i < entitiesToMove.Length; i++)
                    {
                        MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup,
                                       entitiesToMove[i].GetEntityType(), profiler);
                    }
                }
                //otherwise it's a normal static entity descriptor
                else
                {
#if DEBUG && !PROFILER
                    if (correctEntityDescriptorFound == false)
                    {
                        throw new ECSException(INVALID_DYNAMIC_DESCRIPTOR_ERROR.FastConcat(" ID ").FastConcat(fromEntityGID.entityID)
                                               .FastConcat(" group ID ").FastConcat(fromEntityGID.groupID).FastConcat(
                                                   " descriptor found: ", entityInfoView.type.Name, " descriptor Excepted ",
                                                   originalDescriptorType.Name));
                    }
#endif

                    Dictionary <Type, ITypeSafeDictionary> toGroup = null;
                    if (toEntityGID != null)
                    {
                        var toGroupID = toEntityGID.Value.groupID;

                        if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false)
                        {
                            toGroup = _groupEntityDB[toGroupID] = new Dictionary <Type, ITypeSafeDictionary>();
                        }
                    }

                    for (var i = 0; i < entityBuilders.Length; i++)
                    {
                        MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup,
                                       entityBuilders[i].GetEntityType(), profiler);
                    }
                }
            }
        }