Search Unity

Why do those two system conflict?

Discussion in 'Entity Component System' started by wojwen, Nov 1, 2021.

  1. wojwen

    wojwen

    Joined:
    Feb 13, 2018
    Posts:
    38
    Hi,

    I'm trying to learn ECS by making a simple system for managing soldiers and units. I wrote three systems, one for initializing units, one for assigning soldiers to units and the other one for moving all soldiers in unit to this unit's position. Here is the code:
    Unit initialization:
    Code (CSharp):
    1.  
    2. public class UnitInitializationSystem : SystemBase
    3. {
    4.     private Entity _manager;
    5.     private EndInitializationEntityCommandBufferSystem _endInitializationECBSystem;
    6.  
    7.     protected override void OnCreate()
    8.     {
    9.         _manager = EntityManager.CreateEntity();
    10.         EntityManager.AddComponent<UnitManagerTag>(_manager);
    11.         EntityManager.AddBuffer<UnitBufferElement>(_manager);
    12.     }
    13.  
    14.     protected override void OnStartRunning()
    15.     {
    16.         _endInitializationECBSystem = World.GetOrCreateSystem<EndInitializationEntityCommandBufferSystem>();
    17.     }
    18.  
    19.     protected override void OnUpdate()
    20.     {
    21.         var ecb = _endInitializationECBSystem.CreateCommandBuffer().AsParallelWriter();
    22.         var manager = _manager;
    23.         int id = 0;
    24.  
    25.         Entities.ForEach((in UnitData unitData) =>
    26.         {
    27.             if (unitData.UnitId > id)
    28.                 id = unitData.UnitId;
    29.         }).Run();
    30.  
    31.         Entities.WithAll<NewUnitTag>().ForEach((Entity e, int entityInQueryIndex) =>
    32.         {
    33.             ecb.RemoveComponent<NewUnitTag>(entityInQueryIndex, e);
    34.             ecb.AddComponent(entityInQueryIndex, e, new UnitData(id));
    35.             ecb.AppendToBuffer(entityInQueryIndex, manager, new UnitBufferElement(e, id));
    36.         }).ScheduleParallel();
    37.  
    38.         _endInitializationECBSystem.AddJobHandleForProducer(Dependency);
    39.     }
    40. }
    Assigning soldiers to units:
    Code (CSharp):
    1.  
    2. public class AssignToUnitSystem : SystemBase
    3. {
    4.     private Entity _manager;
    5.     private EndSimulationEntityCommandBufferSystem _endSimulationECBSystem;
    6.  
    7.     protected override void OnStartRunning()
    8.     {
    9.         _manager = GetSingletonEntity<UnitManagerTag>();
    10.         _endSimulationECBSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    11.     }
    12.  
    13.     protected override void OnUpdate()
    14.     {
    15.         var units = EntityManager.GetBuffer<UnitBufferElement>(_manager);
    16.         var ecb = _endSimulationECBSystem.CreateCommandBuffer().AsParallelWriter();
    17.         var unitTranslations = new NativeArray<Translation>(units.Length, Allocator.TempJob);
    18.         for (var i = 0; i < units.Length; i++)
    19.             unitTranslations[i] = EntityManager.GetComponentData<Translation>(units[i].UnitEntity);
    20.  
    21.  
    22.         Entities.WithAll<SoldierTag>().WithNone<SharedUnitData>().WithReadOnly(unitTranslations).WithReadOnly(units)
    23.             .ForEach(
    24.                 (Entity e, int entityInQueryIndex, in Translation position) =>
    25.                 {
    26.                     float smallestDist = 1000f; // ugly, but it's only a prototype
    27.                     int closestUnitId = 0;
    28.                     for (var i = 0; i < units.Length; i++)
    29.                     {
    30.                         var distance = math.distance(position.Value, unitTranslations[i].Value);
    31.                         if (distance < smallestDist)
    32.                         {
    33.                             smallestDist = distance;
    34.                             closestUnitId = units[i].UnitId;
    35.                         }
    36.                     }
    37.  
    38.                     ecb.AddSharedComponent(entityInQueryIndex, e, new SharedUnitData(closestUnitId));
    39.                 }).ScheduleParallel();
    40.  
    41.         _endSimulationECBSystem.AddJobHandleForProducer(Dependency);
    42.     }
    43. }
    44.  
    Unit management:
    Code (CSharp):
    1.  
    2. public class UnitManagementSystem : SystemBase
    3. {
    4.     private Entity _manager;
    5.     private EndSimulationEntityCommandBufferSystem _endSimulationECBSystem;
    6.  
    7.     protected override void OnStartRunning()
    8.     {
    9.         _manager = GetSingletonEntity<UnitManagerTag>();
    10.         _endSimulationECBSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    11.     }
    12.  
    13.     protected override void OnUpdate()
    14.     {
    15.         var ecb = _endSimulationECBSystem.CreateCommandBuffer().AsParallelWriter();
    16.         var manager = _manager;
    17.         var units = EntityManager.GetBuffer<UnitBufferElement>(manager);
    18.         NativeArray<Translation> unitTranslations = new NativeArray<Translation>(units.Length, Allocator.TempJob);
    19.         for (var i = 0; i < units.Length; i++)
    20.             unitTranslations[i] = EntityManager.GetComponentData<Translation>(units[i].UnitEntity);
    21.  
    22.         for (var i = 0; i < units.Length; i++)
    23.         {
    24.             var sharedUnitData = new SharedUnitData(units[i].UnitId);
    25.             Entities.WithSharedComponentFilter(sharedUnitData).WithReadOnly(unitTranslations)
    26.                 .ForEach((Entity e, int entityInQueryIndex) =>
    27.                 {
    28.                     ecb.SetComponent(entityInQueryIndex, e, unitTranslations[i]);
    29.                 }).ScheduleParallel();
    30.         }
    31.  
    32.         _endSimulationECBSystem.AddJobHandleForProducer(Dependency);
    33.     }
    34. }
    As you can see I'm using a DynamicBuffer to store all units in the manager entity and a shared component to keep track of which soldiers belong to which unit. The issue is that when I run all these systems together, I get this error every frame:

    I thought this should be solved by using command buffers, but it's not. Also I know I'm not disposing the NativeArrays, but I don't know where should I do it. If anyone knows how to solve any of those issues please let me know. Thanks!
     
    Last edited: Nov 1, 2021
  2. tassarho

    tassarho

    Joined:
    Aug 25, 2019
    Posts:
    75
    i think it's on Unit Management, your "_endSimulationECBSystem.AddJobHandleForProducer(Dependency)" is placed after the for loop which means the job wich is schedule multiple time need to be completed inside the for loop.

    Edit: it also means you will need to create the commandbuffer inside the loop if that's the issue
     
  3. wojwen

    wojwen

    Joined:
    Feb 13, 2018
    Posts:
    38
    Will it work if I just move
    _endSimulationECBSystem.AddJobHandleForProducer(Dependency);
    inside the loop? Or should I use something else instead of
    Dependency
    ?

    Edit: I wouldn't want to create additional command buffers.
     
  4. tassarho

    tassarho

    Joined:
    Aug 25, 2019
    Posts:
    75
    you may want to playce "_endSimulationECBSystem.AddJobHandleForProducer(Dependency);" because you call ".ScheduleParallel();" multiple time but assign them to a jobhandle only once, so let say your loop iterate 3 times, the first will be schedule but the seconde won't have a Dependency to wait (thus throwing an exception) since the "addJobHandle" is outside your loop.

    i don't think you need to create multiple commande buffer after thinking twice, but place your dependency inside the loop anyway so each schedule have its dependency.