Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Iterating over Entities in jobs

Discussion in 'Entity Component System' started by smarrog, May 10, 2020.

  1. smarrog

    smarrog

    Joined:
    Jan 18, 2016
    Posts:
    9
    Hello,
    Help me please to understant how can I convert ComponentSystem to JobComponentSystem.
    Problem is that I can't understand how I can iterate over all Entities with required Component inside job.

    Here is code on Component System - it works

    Code (CSharp):
    1. public class FindTargetForZombieSystem : ComponentSystem {
    2.     protected override void OnUpdate() {
    3.         Entities.WithAll<Zombie>().WithNone<Target>().ForEach((Entity entity, ref Translation translation) => {
    4.             Entities.WithAll<Human>().ForEach((Entity targetEntity, ref Translation targetTranslation, ref HealthData healthData) => {
    5.                // ... some code
    6.         });
    7.     }
    8. }
    And this is my attempt of converting it to JobComponentSystem

    Code (CSharp):
    1. public class FindTargetForZombieSystem : JobComponentSystem {
    2.     private EntityCommandBufferSystem _bufferSystem;
    3.  
    4.     protected override void OnCreate() {
    5.         _bufferSystem = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
    6.     }
    7.    
    8.     [ExcludeComponent(typeof(Target))]
    9.     [RequireComponentTag(typeof(Zombie))]
    10.     [BurstCompile]
    11.     public struct FindTargetJob : IJobForEach<Translation> {
    12.         public EntityCommandBuffer.Concurrent CommandBuffer;
    13.  
    14.         public void Execute(ref Translation translation) {
    15.            // How can I iterate over all Entities with "Human" component here ?
    16.         }
    17.     }
    18.    
    19.     protected override JobHandle OnUpdate(JobHandle inputDeps) {
    20.         var jobHandle = new FindTargetJob {
    21.             CommandBuffer = _bufferSystem.CreateCommandBuffer().ToConcurrent()
    22.         }.Schedule(this, inputDeps);
    23.         _bufferSystem.AddJobHandleForProducer(jobHandle);
    24.         return jobHandle;
    25.     }
    26. }
    So how can i get entities with "Human" inside job ?
     
  2. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,655
    First of all - use SystemBase as (Job)ComponentSystem will be removed eventially. IJobForEach deprecated already and will be removed. You should use Entities.ForEach from SystemBase where Unity improved codegen in comparison with previous versions in (Job)ComponentSystem.
     
    smarrog likes this.
  3. smarrog

    smarrog

    Joined:
    Jan 18, 2016
    Posts:
    9
    Thank you

    But nested Entities.ForEach Lambda expressions are not supported in SystemBase - what is the best way to do it ?
     
    Last edited: May 10, 2020
  4. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,655
    Supported. You just should use WithoutBurst() and Run() to make it mainthreaded and not burst compiled.
     
    smarrog likes this.
  5. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    smarrog likes this.
  6. smarrog

    smarrog

    Joined:
    Jan 18, 2016
    Posts:
    9
    Thank for that link. Looks like my target system is same, but In runtime I have this error
    ArgumentException: The previously scheduled job FindTargetForZombieSystem:<>c__DisplayClass_OnUpdate_LambdaJob0 writes to the NativeArray <>c__DisplayClass_OnUpdate_LambdaJob0.JobData.ecb. You must call JobHandle.Complete() on the job FindTargetForZombieSystem:<>c__DisplayClass_OnUpdate_LambdaJob0, before you can write to the NativeArray safely.
    EntityCommandBuffer was recorded in FindTargetForZombieSystem and played back in Unity.Entities.EndSimulationEntityCommandBufferSystem.


    This happends after trying to add component to EnityBuffer. What wrong with that ?

    Code (CSharp):
    1. protected override void OnCreate() {
    2.         _ecbSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    3.         _humansQuery = GetEntityQuery(
    4.             ComponentType.ReadOnly<Human>(),
    5.             ComponentType.ReadOnly<Translation>());
    6.     }
    7.    
    8.     protected override void OnUpdate() {
    9.         var ecb = _ecbSystem.CreateCommandBuffer().ToConcurrent();
    10.        
    11.         var translationType = GetArchetypeChunkComponentType<Translation>();
    12.         var entityType = GetArchetypeChunkEntityType();
    13.         var chunks = _humansQuery.CreateArchetypeChunkArray(Allocator.TempJob);
    14.        
    15.         Entities
    16.             .WithReadOnly(translationType)
    17.             .WithReadOnly(entityType)
    18.             .WithDeallocateOnJobCompletion(chunks)
    19.             .WithAll<Zombie>()
    20.             .WithNone<Target>()
    21.             .ForEach((Entity entity, int entityInQueryIndex, in Translation translation) => {
    22.                 var position = translation.Value;
    23.                 var closestTarget = Entity.Null;
    24.                 var closestDistance = float.MaxValue;
    25.  
    26.                 for (var chunkIndex = 0; chunkIndex < chunks.Length; chunkIndex++) {
    27.                     var chunk = chunks[chunkIndex];
    28.                     var chunkEntities = chunk.GetNativeArray(entityType);
    29.                     var translations = chunk.GetNativeArray(translationType);
    30.  
    31.                     for (var i = 0; i < chunk.Count; i++) {
    32.                         var target = chunkEntities[i];
    33.                         var targetTranslation = translations[i];
    34.                         var distanceToTarget = math.distance(position, targetTranslation.Value);
    35.  
    36.                         if (closestTarget == Entity.Null || distanceToTarget < closestDistance) {
    37.                             closestTarget = target;
    38.                             closestDistance = distanceToTarget;
    39.                         }
    40.                     }
    41.                 }
    42.                
    43.                 if (closestTarget != Entity.Null) {
    44.                     ecb.AddComponent(entityInQueryIndex, entity, new Target {
    45.                         Entity = closestTarget
    46.                     });
    47.                 }
    48.             }).ScheduleParallel();
    49.     }
     
  7. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,993
    Add
    Code (CSharp):
    1. _ecbSystem.AddJobHandleForProducer(Dependency);
    at the end of OnUpdate().
     
    smarrog likes this.