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

How to properly migrate IJobForEach -> IJobChunk (writing to multiple chunks)?

Discussion in 'Entity Component System' started by alvinwan, Jul 12, 2020.

  1. alvinwan

    alvinwan

    Joined:
    Jan 21, 2018
    Posts:
    34
    The setup: An Enemy attacks the Player. To effect damage, the Enemy decrements the Player's health (EnemySystem_ForEach.cs below). I attempted to rewrite this ForEach as an IJobChunk (EnemySystem_IJobChunk.cs below).

    Problem: The ForEach writes to both the Enemy and the Player chunks. As a result, I tried to pass the health container* to IJobChunk. However, runtime provides an error that says the container must be marked readonly. *health container being ComponentDataFromEntity<HealthComponent>.

    Question: How do I rewrite ForEach as an IJobChunk, when ForEach is writing to chunks of different archetypes?

    Note: This came up after realizing IJobForEach* structs are deprecated. Otherwise, I'd be more than happy to use IJobForEach*.

    Note: One possibility is to include both Player and Enemy in the EntityQuery. In Execute, I would then iterate over all queried entities to find the player first, then iterate over all remaining entities, that are enemies. Is this reasonable? (nvm: This wouldn't work because Enemy and Player have different archetypes, so they wouldn't live in the same Execute call...)

    Note: ECSSamples includes IJobChunk but both exampes as of writing (ex 1, ex 2), afaik, only write to chunks of one archetype at a time.


    Original working code EnemySystem_ForEach.cs

    Code (CSharp):
    1. public class EnemySystem : SystemBase {
    2.     protected override void OnUpdate()
    3.     {
    4.         var healthGroup = GetComponentDataFromEntity<HealthComponent>();
    5.         var player = GetSingletonEntity<PlayerComponent>();
    6.  
    7.         Dependency = Entities.ForEach((
    8.             Entity entity,
    9.             int entityInQueryIndex,
    10.             ref EnemyComponent enemy) =>
    11.         {
    12.             enemy.AttackCount += 1;
    13.  
    14.             var health = healthGroup[player];
    15.             health.Value -= 1;
    16.             healthGroup[player] = health;
    17.         }.Schedule(Dependency);
    18. }
    Attempt at IJobChunk EnemySystem_IJobChunk.cs
    Code (CSharp):
    1. public class EnemySystem : SystemBase {
    2.     EntityQuery EnemyQuery => GetEntityQuery(typeof(EnemyComponent));
    3.  
    4.     struct DuckSystemJob2 : IJobChunk
    5.     {
    6.         // breaks down when I add the following line, as it must be readonly
    7.         public ComponentDataFromEntity<HealthComponent> HealthGroup;
    8.         public ArchetypeChunkComponent<EnemyComponent> EnemyType;
    9.  
    10.         public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    11.        {
    12.            var chunkEnemies = chunk.GetNativeArray(EnemyType);
    13.            for (var i = 0; i < chunk.Count; i++) {
    14.                var enemy = chunkEnemies[i];
    15.                enemy.AttackCount += 1;
    16.                chunkEnemies[i] = enemy;
    17.            }
    18.         }
    19.     }
    20.     protected override void OnUpdate() {
    21.         Dependency = new EnemySystemJob() {
    22.             HealthGroup = GetComponentDataFromEntity<HealthComponent>(),
    23.         }.Schedule(EnemyQuery, Dependency);
    24.     }
    25. }
     
    Last edited: Jul 12, 2020
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,993
    This is not an IJobForEach, this is a lambda Entities.ForEach which is not deprecated.
     
  3. alvinwan

    alvinwan

    Joined:
    Jan 21, 2018
    Posts:
    34
    Thanks for the reply. For sure, I understand Entities.ForEach isn't deprecated.

    My goal is to switch to a class-style job instead of a lambda, for code cleanliness. Since IJobForEach isn't a viable long-term option, I turned to IJobChunk.
     
  4. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    944
    Your job chunk is not doing anything in the execute. That's probably why it tells you it should be read only, because your code is not accutally assigning anything to it.
    Also if you are looking to implement an ability system, feel free to look at my public repo on MGM Ability. (still WIP ;) )
     
  5. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    677
    The equivalent of ForEach.Schedule is JobChunk.ScheduleSingle (they seriously need to follow a unique convention here).
    upload_2020-7-12_13-25-28.png
    In case you wonder why to use ScheduleSingle, you can only use GetComponentDataFromEntity as read-only in parallel jobs.
     
    alvinwan likes this.