Search Unity

ScheduleBatchedJobsAndComplete can only be called from the main thread.

Discussion in 'Entity Component System' started by hakankaraduman, Jun 25, 2019.

  1. hakankaraduman

    hakankaraduman

    Joined:
    Aug 27, 2012
    Posts:
    354
    Hi,

    I'm trying to get up to speed on pure ECS.

    I get the error below with the codes I have, I have pasted all of them here. The error doesn't direct me to any of my custom scripts and I couldn't find a google search for this error. Thanks in advance.

    Below is Error

    Code (CSharp):
    1. UnityException: ScheduleBatchedJobsAndComplete can only be called from the main thread.
    2. Constructors and field initializers will be executed from the loading thread when loading a scene.
    3. Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
    4. Unity.Jobs.JobHandle.Complete () (at C:/buildslave/unity/build/Runtime/Jobs/ScriptBindings/JobHandle.bindings.cs:20)
    5. Unity.Entities.ComponentJobSafetyManager.CompleteWriteDependencyNoChecks (System.Int32 type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentJobManager.cs:352)
    6. Unity.Entities.ComponentJobSafetyManager.CompleteWriteDependency (System.Int32 type) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/ComponentJobManager.cs:377)
    7. Unity.Entities.EntityManager.GetComponentData[T] (Unity.Entities.Entity entity) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/EntityManagerAccessComponentData.cs:41)
    8. MoveToTargetJob.Execute (Unity.Entities.Entity unitEntity, System.Int32 index, Unity.Transforms.Translation& unitTranslation, HasTarget& hasTarget) (at Assets/_4/Jobs/MoveToTargetJob.cs:24)
    9. Unity.Entities.JobForEachExtensions+JobStruct_Process_ECC`3[T,T0,T1].ExecuteChunk (Unity.Entities.JobForEachExtensions+JobStruct_Process_ECC`3[T,T0,T1]& jobData, System.IntPtr bufferRangePatchData, System.Int32 begin, System.Int32 end, Unity.Entities.ArchetypeChunk* chunks, System.Int32* entityIndices) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/IJobForEach.gen.cs:2089)
    10. Unity.Entities.JobForEachExtensions+JobStruct_Process_ECC`3[T,T0,T1].Execute (Unity.Entities.JobForEachExtensions+JobStruct_Process_ECC`3[T,T0,T1]& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at Library/PackageCache/com.unity.entities@0.0.12-preview.33/Unity.Entities/IJobForEach.gen.cs:2059)
    11.  
    Add Target To Unit Job code

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5. using Unity.Jobs;
    6. using Unity.Transforms;
    7. using Unity.Collections;
    8.  
    9. [RequireComponentTag(typeof(UnitTag))]
    10. [ExcludeComponent(typeof(HasTarget))]
    11. public struct AddTargetToUnitJob : IJobForEachWithEntity<Translation>
    12. {
    13.   [DeallocateOnJobCompletion]
    14.   [ReadOnly]
    15.   public NativeArray<Entity> closestTargetEntities;
    16.  
    17.   public EntityCommandBuffer.Concurrent entityCommandBuffer;
    18.  
    19.   public void Execute(Entity entity, int index, ref Translation c0)
    20.   {
    21.     if (closestTargetEntities[index] != Entity.Null) {
    22.       entityCommandBuffer.AddComponent(index, entity, new HasTarget { Value = closestTargetEntities[index] });
    23.     }
    24.   }
    25. }
    26.  
    Find Closest Target Job code

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Jobs;
    5. using Unity.Entities;
    6. using Unity.Transforms;
    7. using Unity.Collections;
    8. using Unity.Mathematics;
    9. using Unity.Burst;
    10.  
    11. [RequireComponentTag(typeof(UnitTag))]
    12. [ExcludeComponent(typeof(HasTarget))]
    13. [BurstCompile]
    14. public struct FindClosestTargetJob : IJobForEachWithEntity<Translation>
    15. {
    16.   [DeallocateOnJobCompletion]
    17.   [ReadOnly]
    18.   public NativeArray<Entity> targetEntities;
    19.  
    20.   [DeallocateOnJobCompletion]
    21.   [ReadOnly]
    22.   public NativeArray<Translation> targetEntityPositions;
    23.  
    24.   public NativeArray<Entity> closestTargetEntities;
    25.  
    26.   public void Execute(Entity entity, int index, [ReadOnly] ref Translation translation)
    27.   {
    28.     var unitPosition = translation.Value;
    29.     Entity closestTarget = Entity.Null;
    30.     float closestDistance = Mathf.Infinity;
    31.     for (var i = 0; i < targetEntities.Length; i++) {
    32.       var distanceBetweenUnitAndTarget = math.distance(targetEntityPositions[i].Value, unitPosition);
    33.       if (distanceBetweenUnitAndTarget < closestDistance) {
    34.         closestDistance = distanceBetweenUnitAndTarget;
    35.         closestTarget = targetEntities[i];
    36.       }
    37.     }
    38.     if (closestTarget != Entity.Null) {
    39.       closestTargetEntities[index] = closestTarget;
    40.     }
    41.   }
    42. }
    43.  
    Move to Target Job code

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5. using Unity.Jobs;
    6. using Unity.Transforms;
    7. using Unity.Collections;
    8. using Unity.Mathematics;
    9.  
    10. [RequireComponentTag(typeof(UnitTag))]
    11. public struct MoveToTargetJob : IJobForEachWithEntity<Translation, HasTarget>
    12. {
    13.   public EntityCommandBuffer.Concurrent buffer;
    14.   public float deltaTime;
    15.  
    16.   public void Execute(Entity unitEntity, int index, ref Translation unitTranslation, ref HasTarget hasTarget)
    17.   {
    18.     // if target does not exists, remove has target component from this entity
    19.     if (World.Active.EntityManager.Exists(hasTarget.Value) == false) {
    20.       buffer.RemoveComponent(index, unitEntity, typeof(HasTarget));
    21.     } else {
    22.       // add direction * speed * deltaTime to unit translation
    23.       var targetPosition = World.Active.EntityManager.GetComponentData<Translation>(hasTarget.Value);
    24.       var direction = targetPosition.Value - unitTranslation.Value;
    25.       unitTranslation.Value += direction * 5f * deltaTime;
    26.       // if distance is below 0.2f, destroy entity, remove hastarget
    27.       var distance = math.distance(unitTranslation.Value, targetPosition.Value);
    28.       if (distance < 0.2f) {
    29.         buffer.RemoveComponent(index, unitEntity, typeof(HasTarget));
    30.         buffer.DestroyEntity(index, hasTarget.Value);
    31.       }
    32.     }
    33.   }
    34. }
    Move To Target System Code

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5. using Unity.Transforms;
    6. using Unity.Mathematics;
    7. using Unity.Jobs;
    8.  
    9. public class MoveToTargetSystem : JobComponentSystem
    10. {
    11.   EntityCommandBufferSystem entityCommandBufferSystem;
    12.   protected override void OnCreate(){
    13.     entityCommandBufferSystem = World.Active.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    14.     base.OnCreate();
    15.   }
    16.   protected override JobHandle OnUpdate(JobHandle inputDeps)
    17.   {
    18.     var moveToTargetJob = new MoveToTargetJob {
    19.       buffer = entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent(),
    20.       deltaTime = Time.deltaTime
    21.     };
    22.     var jobHandle = moveToTargetJob.Schedule(this, inputDeps);
    23.     entityCommandBufferSystem.AddJobHandleForProducer(jobHandle);
    24.     return jobHandle;
    25.   }
    26. }
    Select Target System code

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5. using Unity.Transforms;
    6. using Unity.Mathematics;
    7. using Unity.Jobs;
    8. using Unity.Collections;
    9.  
    10. public class SelectTargetSystem : JobComponentSystem
    11. {
    12.   EntityCommandBufferSystem entityCommandBufferSystem;
    13.   protected override void OnCreate(){
    14.     entityCommandBufferSystem = World.Active.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    15.     base.OnCreate();
    16.   }
    17.  
    18.   protected override JobHandle OnUpdate(JobHandle inputDeps)
    19.   {
    20.     var targetEntitiesQuery = GetEntityQuery(typeof(TargetTag), typeof(Translation));
    21.     NativeArray<Entity> targetEntities = targetEntitiesQuery.ToEntityArray(Allocator.TempJob);
    22.     NativeArray<Translation> targetEntityPositions = targetEntitiesQuery.ToComponentDataArray<Translation>(Allocator.TempJob);
    23.     NativeArray<Entity> closestTargetEntities = new NativeArray<Entity>(targetEntities.Length, Allocator.TempJob);
    24.     FindClosestTargetJob findClosestTargetJob = new FindClosestTargetJob {
    25.       targetEntities = targetEntities,
    26.       targetEntityPositions = targetEntityPositions,
    27.       closestTargetEntities = closestTargetEntities
    28.     };
    29.     var jobHandle = findClosestTargetJob.Schedule(this, inputDeps);
    30.     AddTargetToUnitJob addTargetToUnitJob = new AddTargetToUnitJob {
    31.       entityCommandBuffer = entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent(),
    32.       closestTargetEntities = closestTargetEntities
    33.     };
    34.     jobHandle = addTargetToUnitJob.Schedule(this, jobHandle);
    35.     entityCommandBufferSystem.AddJobHandleForProducer(jobHandle);
    36.     return jobHandle;
    37.   }
    38. }
    39.  
    Scene Manager code to spawn units and targets

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Collections;
    5. using Unity.Entities;
    6. using Unity.Transforms;
    7. using Unity.Mathematics;
    8. using Unity.Rendering;
    9. public class SceneManager4 : MonoBehaviour
    10. {
    11.   public Mesh unitMesh;
    12.   public Material unitMaterial;
    13.   public Mesh targetMesh;
    14.   public Material targetMaterial;
    15.   EntityManager em;
    16.  
    17.   public int unitAmount = 10;
    18.   public int targetAmount = 10;
    19.   public float spawnRate = 2.0f;
    20.   float spawnTimer = 0.0f;
    21.  
    22.   void Awake () {
    23.     em = World.Active.EntityManager;
    24.   }
    25.   void Start()
    26.   {
    27.     SpawnUnits();
    28.     SpawnTargets();
    29.   }
    30.  
    31.   void Update(){
    32.     // spawnTimer += Time.deltaTime;
    33.     // if (spawnTimer >= spawnRate) {
    34.     //   SpawnTargets();
    35.     //   spawnTimer = 0.0f;
    36.     // }
    37.   }
    38.  
    39.   void SpawnUnits(){
    40.     var unitEntities = new NativeArray<Entity>(unitAmount, Allocator.Temp);
    41.     var unitArchetype = em.CreateArchetype(
    42.       typeof(RenderMesh),
    43.       typeof(LocalToWorld),
    44.       typeof(Translation),
    45.       typeof(UnitTag)
    46.     );
    47.     em.CreateEntity(unitArchetype, unitEntities);
    48.     foreach (var unit in unitEntities) {
    49.       em.SetComponentData(unit, new Translation { Value = new float3 { x = UnityEngine.Random.Range(-20, 20), y = UnityEngine.Random.Range(-20, 20), z = 0 } });
    50.       em.SetSharedComponentData(unit, new RenderMesh { mesh = unitMesh, material = unitMaterial });
    51.     }
    52.     unitEntities.Dispose();
    53.   }
    54.   void SpawnTargets(){
    55.     var targetEntities = new NativeArray<Entity>(targetAmount, Allocator.Temp);
    56.     var targetArchetype = em.CreateArchetype(
    57.       typeof(RenderMesh),
    58.       typeof(LocalToWorld),
    59.       typeof(Translation),
    60.       typeof(TargetTag)
    61.     );
    62.     em.CreateEntity(targetArchetype, targetEntities);
    63.     foreach (var target in targetEntities) {
    64.       em.SetComponentData(target, new Translation { Value = new float3 { x = UnityEngine.Random.Range(-20, 20), y = UnityEngine.Random.Range(-20, 20), z = 0 } });
    65.       em.SetSharedComponentData(target, new RenderMesh { mesh = targetMesh, material = targetMaterial });
    66.     }
    67.     targetEntities.Dispose();
    68.   }
    69. }
    70.  
    Note: project was working until I implement the move to target system
     
  2. Razmot

    Razmot

    Joined:
    Apr 27, 2013
    Posts:
    346
    do not use "World.Active.EntityManager.Exists" in a job, instead pass a ComponentDataFromEntity<HasTarget > and use its exists() method
     
    hakankaraduman likes this.
  3. hakankaraduman

    hakankaraduman

    Joined:
    Aug 27, 2012
    Posts:
    354
    Thanks for your reply. I couldn't find how to use this, all examples are using [Inject] attribute and could not find an updated implementation for this. Can you help if you have time for this?
     
  4. Razmot

    Razmot

    Joined:
    Apr 27, 2013
    Posts:
    346
    Here you are :

    Code (CSharp):
    1.  
    2.   protected override JobHandle OnUpdate(JobHandle inputDeps)
    3.         {
    4.      var endJob = new EndJob()
    5.             {
    6.                 stitchedT = GetComponentDataFromEntity<StitchT_Done>(true),
    7.                 stitchedB = GetComponentDataFromEntity<StitchB_Done>(true),
    8.                 CommandBuffer = ecb,//finishBarrier.CreateCommandBuffer().ToConcurrent()
    9.             };
    Code (CSharp):
    1.        private struct EndJob : IJobForEachWithEntity<VoxelChunk>
    2.         {
    3.             [ReadOnly] public ComponentDataFromEntity<StitchT_Done> stitchedT;
    4.             [ReadOnly] public ComponentDataFromEntity<StitchB_Done> stitchedB;
    5.  
    6.             [WriteOnly] public EntityCommandBuffer.Concurrent CommandBuffer;
    7.  
    8.             public void Execute(Entity e, int i, [ReadOnly] ref VoxelChunk chunk)
    9.             {
    10.                 bool done = true;
    11.  
    12.                 if (chunk.HasUp) done &= stitchedT.Exists(e);
    13.                 if (chunk.HasDown) done &= stitchedB.Exists(e);
    ...
     
    hakankaraduman likes this.
  5. hakankaraduman

    hakankaraduman

    Joined:
    Aug 27, 2012
    Posts:
    354
    Thanks for the help. I see, this check if the StitchT_Done component exists on the entity e, is that right?

    In my situation, I have a reference to an entity in the HasTarget component. I need to check if that referenced entity exists or not, so if it is destroyed or not.
     
  6. Razmot

    Razmot

    Joined:
    Apr 27, 2013
    Posts:
    346
    You can do exists on any entity, it does not have to be the argument of execute ( 'e' in my case)
     
    hakankaraduman likes this.