Search Unity

Feedback EntityCommandBuffer.CreateEntity reference in IComponentData

Discussion in 'Entity Component System' started by Justin_Larrabee, Jun 10, 2019.

  1. Justin_Larrabee

    Justin_Larrabee

    Joined:
    Apr 24, 2018
    Posts:
    106
    I just have to say, the automatic fixup of Entity references inside IComponentData when calling Add/SetComponent on EntityCommandBuffer is nothing short of brilliant. Well done.

    I would however highly recommend adding a note about this in the EntityCommandBuffer documentation. It is not immediately obvious that the Entity returned by CreateEntity is something you should not store a reference to unless you do so via Add/SetComponentData on the *same* EntityCommandBuffer.
     
    Enzi likes this.
  2. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    966
    Huh, since when is this happening? I have worked around that issue and made 2 threads about it. :(

    Direct references are still not possible, right? But if I put the new entity in a component it gets automatically updated from Id values like -1 that we are seeing in the debugger?

    This is great info! I need to utilize this.
     
  3. Justin_Larrabee

    Justin_Larrabee

    Joined:
    Apr 24, 2018
    Posts:
    106
    If you call EntityCommandBuffer.CreateEntity and then store that immediately in a ref IComponentData in say, an IJobForEach job -- yes your reference will be bad (it'll have a command buffer relative value like Entity{Version=0, Index=-1...}). If you set that same IComponentData via a call to EntityCommandBuffer.SetComponent, any Entity references in your IComponentData get fixed-up when the commands are played back.
     
  4. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    966
    Yes, it works! Thanks so much!

    My spawner system now puts a SpawnComplete tag with the new instanced entity on the SpawnCommand so PostProcessing_ comps know what to work on.
    Before I had to put a SpawnComplete with the parent on the instance and it was a pain to get all the data back or even knowing what data was there in the first place.

    Now it's all good. Finally! Haha
     
  5. Justin_Larrabee

    Justin_Larrabee

    Joined:
    Apr 24, 2018
    Posts:
    106
    Glad my somewhat accidental discovery was useful. If you are curious on implementation details at all, look in EntityCommandBufferData.AddEntityComponentCommand -- you can see how they explicitly check if an IComponentData needs Entity fixups. Clever stuff.
     
    recursive likes this.
  6. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    Thank you. Here is a code (line 30) reference showing the working code (and a single-threaded version for comparison)

    Code (CSharp):
    1.             ////single threaded
    2.             //{
    3.             //    Entities.ForEach((int entityInQueryIndex, ref Spawner spawner, in Translation trans, in Rotation rot) =>
    4.             //    {
    5.             //        if (!EntityManager.Exists(spawner.spawnObject))
    6.             //        {
    7.             //            //doesn't exist, so spawn a copy of the prefab, using transform details from the spawner
    8.             //            spawner.spawnObject = EntityManager.Instantiate(spawner.spawnPrefab);
    9.             //            EntityManager.SetComponentData(spawner.spawnObject, trans);
    10.             //            EntityManager.SetComponentData(spawner.spawnObject, rot);
    11.             //        }
    12.             //    })
    13.             //    .WithStructuralChanges().Run();
    14.             //}
    15.  
    16.             //parallel
    17.             {
    18.                 var ecbSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    19.                 var ecb = ecbSystem.CreateCommandBuffer().AsParallelWriter();
    20.                 Entities
    21.                 .ForEach((Entity spawnerEnt, int entityInQueryIndex, ref Spawner spawner, in Translation trans, in Rotation rot) =>
    22.                 {
    23.                     if (!HasComponent<Translation>(spawner.spawnObject))  //works in a Parallel ForEach(), makes sure that Entity and Translation component exist
    24.                     {
    25.                         //doesn't exist, so spawn a copy of the prefab, using transform details from the spawner
    26.                         spawner.spawnObject = ecb.Instantiate(entityInQueryIndex, spawner.spawnPrefab); //creates a TEMPORARY entity reference.   needs to be fixed up (see below)
    27.                         ecb.SetComponent(entityInQueryIndex, spawner.spawnObject, trans);
    28.                         ecb.SetComponent(entityInQueryIndex, spawner.spawnObject, rot);
    29.  
    30.                         ecb.SetComponent(entityInQueryIndex, spawnerEnt, spawner); //need re-copy the spawner component back onto itself, to fix up the spawner.spawnObject reference.
    31.                     }
    32.                 })
    33.                 .ScheduleParallel();
    34.                 ecbSystem.AddJobHandleForProducer(this.Dependency);
    35.             }