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. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

EntityManager + Jobs?

Discussion in 'Entity Component System' started by madks13, Sep 3, 2018.

  1. madks13

    madks13

    Joined:
    May 8, 2016
    Posts:
    173
    Hello,

    i have run into a wall wanting to jobify a process in my game. Let me explain :
    - I have blocks, representing tiles in a 2D world
    - I have 2 systems in charge of managing the lifecycle of each block
    - A block's lifecycle is as follows :
    1. Create entity from block Archetype
    2. Setup block position and other basic data
    3. Fill data from persistence storage or generate with generator (very slow) based on block position and some other settings
    4. Add texture based on block type
    5. If entity is moved, repeat from 2.
    6. If block is not needed, remove it

    - Before jobification, i was adding/removing data components to activate the concerned systems only when needed
    - With the jobification, this becomes quite hard.

    What i tried to do is add EntityManager reference to a Job, so i can set up components (adding/removing them to activate/deactivate systems), but of course it doesn't work.

    Since EntityManager doesn't have mass add/remove component similar to mass instantiation, i was wondering how i could do this.

    I need to jobify this since i will be managing around 40k blocks per player (if the chunk of each player don't intersect).

    Is there a way to remove/add components to entities in a job at all?
    Also, i will need to call the methods to load block data from persistence or to generate them with the generator. But they are non-blittable classes/structs. Can i use shared component data to pass them to jobs?
     
  2. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    EntityCommandBuffer
     
  3. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    What you mean by EntityManager mass? You mean adding mass to entity as an component?

    If you want to add remove components in a job, or add / remove entities, you need use CommandBuffer, instead EntityManager. There is few post already on the forum on that, which will guide you, if that what you need.
     
  4. madks13

    madks13

    Joined:
    May 8, 2016
    Posts:
    173
    @Antypodish
    - It's "EntityManager mass add/remove". "mass" being a word used to indicate something in great quantities. In this case it's adding or removing component data. In my case, creating new blocks will require removing component data used for initializing said blocks, and adding information to display said data. Since chunks are anywhere between 12k to 40k blocks, i think it's safe to use the word "mass" to indicate it's a large quantity.

    @Antypodish. @eizenhorn
    Thanks for the advice. I've seen mention of the command buffer before, but didn't concern myself with it. Will take a look as soon as i can.
     
  5. madks13

    madks13

    Joined:
    May 8, 2016
    Posts:
    173
    @Antypodish, @eizenhorn So i took a look at EntityCommandBuffer. It seems to work ok, but now i'm getting all kinds of errors, most of them being index out of range errors. Not sure if it's caused by me wrongly using ECB or the job. Here is what i have currently :

    The Job :
    Code (CSharp):
    1. [BurstCompile]
    2.     public struct BlockLoadJob : IJobParallelFor
    3.     {
    4.         //Debug variable
    5.         public int TextureMaxSize;
    6.  
    7.         //Generation data
    8.         public int Seed;
    9.         [ReadOnly] public NativeArray<Position> Positions;
    10.  
    11.         //Entity modification variables
    12.         [ReadOnly] public EntityCommandBuffer.Concurrent Command;
    13.         public EntityArray Blocks;
    14.  
    15.         public void Execute(int index)
    16.         {
    17.             //Get position data
    18.             var data = Positions[index];
    19.  
    20.             //Generate code based on position and seed
    21.             var rand = new Unity.Mathematics.Random((uint)(data.Value.x + data.Value.y + data.Value.z + Seed));
    22.             int code = rand.NextInt(0, TextureMaxSize);
    23.  
    24.             //Add component so the view system can update display
    25.             var entity = Blocks[index];
    26.             Command.AddComponent(entity, new BlockData { Code = code });
    27.         }
    28.     }

    The system :

    Code (CSharp):
    1.   [UpdateAfter(typeof(InitializingSystem))]
    2.     public class BlockLoadSystem : JobComponentSystem
    3.     {
    4.         private struct BlocksData
    5.         {
    6.             public readonly int Length;
    7.             public EntityArray Entity;
    8.             public ComponentDataArray<Position> Position;
    9.             public ComponentDataArray<BlockLoad> Load;
    10.         }
    11.  
    12.         [Inject] private BlockLoadBarrierSystem Barrier;
    13.         [Inject] private BlocksData m_Blocks;
    14.  
    15.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    16.         {
    17.             return new BlockLoadJob
    18.             {
    19.                 Blocks = m_Blocks.Entity,
    20.                 Positions = m_Blocks.Position.GetChunkArray(0, m_Blocks.Length),
    21.                 Command = Barrier.CreateCommandBuffer(),
    22.                 Seed = 0,
    23.                 TextureMaxSize = ResourceManager.Textures.Length
    24.             }.Schedule(m_Blocks.Length, 1, inputDeps);
    25.         }
    26.     }
    27.  
    28.     public class BlockLoadBarrierSystem : BarrierSystem { }

    The errors i'm getting are of several kind :

    Code (CSharp):
    1. IndexOutOfRangeException: Index 3200 is out of range of '325' Length.
    Code (CSharp):
    1. InvalidOperationException: The native container has been declared as [ReadOnly] in the job, but you are writing to it.
    Edit : i have had several other errors like System.Int32 not being supported by Burst compilator. I tried removing the BurstCompileAttribute and now only the index out of range remains. I'm not sure what is wrong with using int inside a burst compiled job. I've seen many code examples using them inside burst compiled jobs.
     
    Last edited: Sep 4, 2018
  6. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    Yep, I mentioned about the issue here as well.

    Dungeon Sample (0.0.10): IndexOutOfRangeException: Index was outside the bounds of the array.
    https://forum.unity.com/threads/dun...x-was-outside-the-bounds-of-the-array.549934/

    Thing is, it seams the issue is not constant. Sometimes is sometimes not. Or there are certain conditions for it.
    Yep, thats completely fine and makes sense. I just misinterpret your initial meaning.
     
  7. madks13

    madks13

    Joined:
    May 8, 2016
    Posts:
    173
    @Antypodish : except in the dungeon sample, i have no exceptions. It works as intended for me i guess.
     
  8. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    Out of interest, did you try click on entities in Entities Debugger?
    Specially the very last one, with highest index. (assuming preview 11)
    That was, when I started getting errors.
     
  9. madks13

    madks13

    Joined:
    May 8, 2016
    Posts:
    173
    @Antypodish Yes, when i go to the entity debugger, select an enitity, and switch to the inspector, i get some error messages. But i guess that might not be as critical, since the debugger integration with the inspector does not forcibly impact the game itself.
     
  10. madks13

    madks13

    Joined:
    May 8, 2016
    Posts:
    173
    @Antypodish do you perhaps know how AddSharedComponentData works? I changed my code to make it simpler and i'm getting weird error messages :

    the code in my BlockCreateSystem :
    Code (CSharp):
    1. var block = EntityManager.CreateEntity(BootStrap.BlockArchetype);// blocks[idx];
    2.                 var position = new Position { Value = new float3(pos.Value.x /*+ x*/, pos.Value.y /*+ y*/, pos.Value.z) };
    3.                 var bData = new BlockData { Code = 1 };
    4.  
    5.  
    6.                 EntityManager.AddComponentData(block, position);
    7.                 EntityManager.AddComponentData(block, scale);
    8.                 EntityManager.AddComponentData(block, bData);
    9.                 EntityManager.AddSharedComponentData(entity, BootStrap.BlockLook);
    I'm adding BlockData component, to let the BlockViewSystem set up the MeshInstancedRenderer correctly.

    Code (CSharp):
    1. for (int i = 0; i < m_Data.Length; i++)
    2.             {
    3.                 var data = m_Data.Block[i];
    4.  
    5.                 var textureData = ResourceManager.Textures.First(t => t.Code == data.Code);
    6.  
    7.                 var entity = m_Data.Entities[i];
    8.  
    9.                 EntityManager.SetSharedComponentData(entity, textureData.MIR);
    10.                 EntityManager.RemoveComponent<BlockData>(entity);
    11.  
    12.                 m_Data.Block[i] = data;
    13.             }
    However, i'm getting the error that MIR wasn't added to the entity.


    ArgumentException: The component has not been added to the entity. Unity.Entities.EntityDataManager.AssertEntityHasComponent


    Somehow i can't add MeshInstanceRenderer if it's not in bootstrap.
     
  11. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    A bit off topic, I suppose ;)

    But, to answer your question (I hope) ...
    At the momemnt In bootstrap I got something like that

    Code (CSharp):
    1. [ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType.AfterSceneLoad ) ]
    2.         static public MeshInstanceRenderer playerRenderer ;
    3.  
    4.         public static void InitializeAfterSceneLoad ()
    5.         {
    6.             playerRenderer = _GetRendererFromPrefab ( "Prefab01" ) ;
    7.             _NewGame () ;
    8.         }
    Then in a system I got
    Code (CSharp):
    1. MeshInstanceRenderer renderer = Bootstrap.playerRenderer ;
    2. commandsBuffer.CreateEntity () ;
    3. commandsBuffer.AddSharedComponent ( renderer ) ;
    Or
    Code (CSharp):
    1. MeshInstanceRenderer renderer = Bootstrap.playerRenderer ;
    2. commandsBuffer.AddSharedComponent ( entity, renderer ) ;