Search Unity

How to store entities created through command buffer?

Discussion in 'Entity Component System' started by davenirline, Feb 24, 2020.

  1. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    982
    I want to store entities created through command buffer (like storing them in a NativeHashMap), but the entity returned using EntityCommandBuffer.CreateEntity() is not yet the actual entity of the created one, right. The actual entity is created when the command buffer is played back. How do you then get that created entity and store it?
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    ECB supports remapping so you can store them on entities

    Code (CSharp):
    1. // This totally works
    2. var newEntity = this.CommandBuffer.CreateEntity(entityInQueryIndex);
    3. this.CommandBuffer.SetComponent(entityInQueryIndex, entity, new Component {Entity = newEntity});
    4.  
    5. // Also for buffers
    6. var buffer = this.CommandBuffer.AddBuffer<ComponentBuffer>(entityInQueryIndex, entity);
    7.  
    8. for(var i = 0; i < 10; i++) {
    9.     var newEntity = this.CommandBuffer.CreateEntity(entityInQueryIndex);
    10.     buffer.Add(new ComponentBuffer {Entity = newEntity});
    11. }
    12.  
    If you need it in container just store them next frame, or better just re-create the container every frame because storing them in a container is usually dumb.
     
    Last edited: Feb 24, 2020
    davenirline likes this.
  3. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    982
    My use case is I wanted to store a mapping of tile position to entity. When an entity is already created at a certain position, I should no longer create it. How can I do this without using a container?
     
  4. siggigg

    siggigg

    Joined:
    Apr 11, 2018
    Posts:
    247
    Add a "NewTile" component to the entity, then have a system query for those and either add it to the mapping (and then remove the component) or delete the entity (if another one is already there).
     
    davenirline likes this.
  5. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    982
    That's one solution but it's sync heavy.
     
  6. vectorized-runner

    vectorized-runner

    Joined:
    Jan 22, 2018
    Posts:
    398
    Maybe you could hold your tile positions in NativeArray<bool> and each time you create an entity convert the position to index and set it to true, and you can check if you need to create or not.
     
    davenirline likes this.
  7. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    991
    Does it matter to know which entity was created for this tile or knowing one was is enougth?
     
  8. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    982
    Only knowing is enough.
     
  9. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    991
    And I assume you know to wnich tile you'll put the entity when creating it so just havin a native list of tile coordinate should be enougth. No ?
    Just create a NativeList<TileCoordinate>(Allocator.Persistent), when your code want to create the an entity, first check that it was not done for the coordiante you plan to assigne the entity to and if the coordinate are not in the nativelist, create the enttiy and add the coordinate to the list.

    Same thing can be done when removing an entity from a tile, get the coordinate, remove them from the native list and destroy the entity.
     
  10. Curlyone

    Curlyone

    Joined:
    Mar 15, 2018
    Posts:
    41
    You could have singleton buffer, and put used tile positions to there. and when you want to create an entity in certain position you can check that buffer and check if tile position is there, if its there that means there is already a created entity there, if not then that tile position is available.

    Lets say this is our tile position data

    Code (CSharp):
    1.  
    2. public struct MapTilePosition : IComponentData
    3. {
    4.     public int X;
    5.     public int Y;
    6. }
    7.  
    This is our buffer that holds the used positions, please not that we have to implement IEquatable<TilePositions> to check if tiles are the same.

    Code (CSharp):
    1.  
    2. public struct TilePositions : IBufferElementData, IEquatable<TilePositions >
    3. {
    4.     public MapTilePosition position;
    5.  
    6.     public override bool Equals(object obj)
    7.     {
    8.         switch (obj)
    9.         {
    10.             case TilePositions tile:
    11.                 return Equals(tile);
    12.             default:
    13.                 return false;
    14.         }
    15.     }
    16.  
    17.     public override int GetHashCode()
    18.     {
    19.         return tile.GetHashCode();
    20.     }
    21.     public bool Equals(TilePositions other)
    22.     {
    23.         if (position.X == other.position.X && position.Y == other.position.Y) return true;
    24.         return false;
    25.     }
    26.     public static bool operator ==(TilePositions left, TilePositions right)
    27.     {
    28.         return left.Equals(right);
    29.     }
    30.     public static bool operator !=(TilePositions left, TilePositions right)
    31.     {
    32.         return !(left == right);
    33.     }
    34.  
    35. }
    36.  
    We can get singleton buffer like this:

    Code (CSharp):
    1.  
    2.             var tilePositionsEntity = GetSingletonEntity<TilePositions>();
    3.             var tilePositionBuffers = GetBufferFromEntity<TilePositions>();
    4.             var tilePositions = tilePositionBuffers[tilePositionsEntity ];
    5.  
    note that if you want to reach TilePositions buffer from multiple systems, you need to access it inside the job and not main thread, so "var tilePositions = tilePositionBuffers[tilePositionsEntity ];" has to be inside ForEach

    now lets say you want to create your entity
    (Contains method requires your struct to have IEquatable<T> interface implemented)
    (You can also use IndexOf() method, if it retuns -1 that means it is not found.)
    Code (CSharp):
    1.  
    2.             //We are inside ForEach
    3.             var newTilePosition = tilePositionFromSomewhere;
    4.             var isUnavailable = tilePositions.AsNativeArray().Contains(newTilePositions);
    5.             //If contains return true, so that means its unavailable.
    6.             if(isUnavailable){
    7.              
    8.              }
    9.              //If contains return false that means it is available, you can create your entity and add your new tile to buffer
    10.              else{
    11.                    //Create new entity
    12.                    //And add your tile to buffer
    13.                   tilePositions.Add(newTilePosition);
    14.              }
    15.  
    16.            //When scheduling your job you have to put .WithNativeDisableParallelForRestriction(tilePositions)
    17.  
    Of course this only works if entity you create will stay on same tile in it's lifetime, otherwise you might find try to look for different solutions, you could possibly do that by extending TilePositions buffer a little bit, and give your entity a data component that has what index of the buffer they should update when they move.

    Hope it helps^^
     
    davenirline likes this.