Search Unity

Question How to get data from a burst job execution

Discussion in 'Entity Component System' started by Niter88, Jan 25, 2023.

  1. Niter88

    Niter88

    Joined:
    Jul 24, 2019
    Posts:
    112
    Hello. I'm trying to make some tests on DOTS/ECS/... to get my head around it. Upon searching I'm coming across deprecated tutorials and answers or no answer at all.

    I'm trying to make a prototype of a tycoon game in which I spawn a bunch of money producers (entities) and each producer keeps adding to a global total value (through a system). I could make a system in which every entity has it's own data and keep updating it, but there is another thing I need to do.

    I need to update the global money for each entity that has a moneyProducer component. I also want to discuss on the available and best ways(performant) to do it.

    PS: I've confirmed that the job is executing and adding to it's internal variables but I can't read it from the correct instance (or equivalent).

    What I've tried so far:
    • Scheduling or Running the job, then reading the value from it like
    job.value

    The value is always default, even after job execution.
    • Handing the variable to the struct by reference.
    Can't do that.
    • Making an interface so the Job can send me a result.
    Can't do that either.
    • I've tried every single option of executing and completing the code I could get my hands on. To no avail.

    Things I'm still thinking about
    • I could try to have a component just to keep the global money and send it to the job, that's quite obvious. I don't know if that's the right approach on data oriented.
    • Some time ago I've read/watched of having global public fields on ECS. Is this accomplished by components? (look like).

    The system:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using Unity.Entities;
    4. using Unity.Burst;
    5. using Unity.Collections;
    6. using Unity.Burst.Intrinsics;
    7. using Unity.Jobs;
    8.  
    9. public partial class MoneyProduceSystem : SystemBase
    10. {
    11.     private EntityQuery query;
    12.  
    13.     public float moneyTotal = 0f;
    14.  
    15.     protected override void OnCreate()
    16.     {
    17.         this.query = GetEntityQuery(typeof(ProducerMoney));
    18.     }
    19.  
    20.     protected override void OnUpdate()
    21.     {
    22.         this.query = GetEntityQuery(typeof(ProducerMoney));
    23.         //Debug.Log("Scheduling job");
    24.         UpdateMoneyJob jobMoney = new UpdateMoneyJob()
    25.         {
    26.             money =  moneyTotal,
    27.             deltaTime = SystemAPI.Time.DeltaTime,
    28.             producerType = GetComponentTypeHandle<ProducerMoney>()
    29.         };
    30.         //this.Dependency = jobMoney.ScheduleParallel(this.query, this.Dependency);
    31.         //this.Dependency.Complete();
    32.  
    33.         //jobMoney.Run(this.query);
    34.  
    35.         JobHandle handle = jobMoney.ScheduleParallel(this.query, this.Dependency);
    36.         handle.Complete();
    37.        
    38.  
    39.         Debug.Log($"returned money {jobMoney.money} completed? {handle.IsCompleted}");
    40.         moneyTotal = jobMoney.money;
    41.     }
    42.  
    43.     [BurstCompile]
    44.     public partial struct UpdateMoneyJob : IJobChunk
    45.     {
    46.         public float money;
    47.         public float deltaTime;
    48.         [ReadOnly]
    49.         public ComponentTypeHandle<ProducerMoney> producerType;
    50.  
    51.         public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
    52.         {
    53.             NativeArray<ProducerMoney> producers = chunk.GetNativeArray(ref this.producerType);
    54.  
    55.             for (int i = 0; i < chunk.Count; ++i)
    56.             {
    57.                 //ProducerMoney producer = producers[i];
    58.  
    59.                 this.money += producers[i].moneyProduced;
    60.             }
    61.             Debug.Log($"Money {this.money}");
    62.         }
    63.     }
    64. }
    65.  
    The component:
    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3. public struct ProducerMoney : IComponentData
    4. {
    5.     public float moneyProduced;
    6. }
     
  2. Niter88

    Niter88

    Joined:
    Jul 24, 2019
    Posts:
    112
    Found out that it works if I
    Code (CSharp):
    1.         float tempMoney = 0;
    2.         this.Entities.ForEach((in ProducerMoney producer) => {
    3.             tempMoney += producer.moneyProduced;
    4.         }).Run();
    5.         Debug.Log($"Money produced {tempMoney}");
    After reading this doc (https://coffeebraingames.wordpress.com/2022/03/27/why-job-structs-are-better-than-entities-foreach/) about how much greater struct jobs are I didn't expect my old foreach to be the solution.
    Is there any way to make it with the original structure?
     
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    NativeReference
     
  4. HyperionSniper

    HyperionSniper

    Joined:
    Jun 18, 2017
    Posts:
    30
    Your job doesn't work because Job structs are copied before being scheduled. The reason updating NativeArrays from within a job works is because NativeArrays only store pointers to an outside buffer and don't contain the data directly.
     
    Niter88, ThatDan123 and elliotc-unity like this.