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); }
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); }
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); } }
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(); } } }
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(); } } } } }
/// <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); }