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. Dismiss Notice

I want to create an entity and store it in a dynamic buffer

Discussion in 'Entity Component System' started by THEplayer321, Dec 16, 2020.

  1. THEplayer321

    THEplayer321

    Joined:
    Nov 3, 2019
    Posts:
    2
    i have been having a lot of problems creating an inventory sistem on ECS.
    what i want to achieve is:
    • inventory component that has a list of items.
    • the items have a name, an entity and an amount int for the amount of that item.
    • being able to create an entity and save it in the inventory as a new item
    the problem i have is when i have to save it, it gives me many different errors trying different things.

    Code (CSharp):
    1. public class PlantFoodSystem : SystemBase
    2. {
    3.     EndSimulationEntityCommandBufferSystem ei_ECB;
    4.     EntityManager EM;
    5.     protected override void OnCreate()
    6.     {
    7.         ei_ECB = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    8.  
    9.         EM = World.EntityManager;
    10.     }
    11.     protected override void OnUpdate()
    12.     {
    13.         Debug.Log("starting frame");
    14.         var ecb = ei_ECB.CreateCommandBuffer();
    15.         float TimeFrame = UnityEngine.Time.deltaTime;
    16.      
    17.  
    18.         Entities.WithAll<Initialize>().ForEach((Entity E, ref PlantFoodSource foodSource) =>
    19.         {
    20.          
    21.  
    22.             Debug.Log("Start Phase");
    23.             Entity newE = EM.CreateEntity();
    24.             EM.AddComponent<Edible>(newE);
    25.             EM.AddComponentData(newE, new Edible(10,5));
    26.             Debug.Log("Entity created ID:" + newE);
    27.  
    28.             DynamicBuffer<Item> NewI = EM.AddBuffer<Item>(E);
    29.             Item bry = new Item("Berry", newE, 0);
    30.             NewI.Add(bry);
    31.          
    32.             Debug.Log("item added");
    33.             foodSource.FoodMax = 10;
    34.             foodSource.GrowthTime = 5;
    35.             ComponentDataFromEntity<InventoryComponent> inventoryArray = GetComponentDataFromEntity<InventoryComponent>(false);
    36.             inventoryArray[E] = new InventoryComponent(NewI);
    37.             EM.RemoveComponent<Initialize>(E);
    38.          
    39.  
    40.         }).WithStructuralChanges().WithoutBurst().Run();
    41. }
    gets me "InvalidOperationException: The NativeArray has been deallocated, it is not allowed to access it"
    pointing at line 36:
    Code (CSharp):
    1. inventoryArray[E] = new InventoryComponent(NewI);
    as the heart of the problem

    if i try to create the entity in a commandbuffer and save it i get
    "NullReferenceException: Object reference not set to an instance of an object"

    i ran out of ideas, and everywhere i look at they never try to create an entity AND store it at the same time

    the only other way i see it is to create another component that stores the inventory and the next frame saves its entity destroying the component afterwards. But that seems too troublesome

    i would really appreciate some help.
     
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,937
    Have you tried EntityCommandBuffer.AppendToBuffer()? It is capable of remapping entity values.
     
  3. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,541
    created entities via ECB don't exists yet in very same frame. There is lag of one frame, before entities are created.
    So you need take that into account.

    When you create new entities, use either archetype, or prefab entity, to instantiate.
    You would remove things like EM.AddComponent<Edible>(newE);, which will affect performance negatively, if executing often / many times.

    You can use EM to create many entities, before actual job. You can store them in NativeArray for example and reuse in a jobs.

    But by a look, you can just use ECB.instantiate, and make sure, you use ECB, instead EM.
    You will need job index for ECB inside foreach job.
     
  4. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Not exactlly like that. The entity does not exist before the ECB is playedback, but it could exist in the current frame. If you Run a system in InitializationSystemGroup and create entity with EndInitializationCommandBufferSystem. the entity exist in Simulation and presentation of the current frame.
    It's perfectly safe to use that entity in a later system in simulation system group in the same frame.

    And If you don't care if there is a structural change. You can create entity immediately. But using EntityManager to create entity in a for loop is a vary bad idea indeed. But you can create a EntityCommandBuffer in the system by
    var ecb=new EntityCommandBuffer()
    and put command in it, and after your loop you can playback the ecb immediately via
    ecb.Playback(EntityManager)

    so it is done in an immediate and fast fasion.
     
    iamarugin and Antypodish like this.
  5. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,541
    You are absolutely right.
    I just wanted to avoid bringing this in at this point, seeing OP is just starting using DOTS.
     
  6. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    I assume this error is from trying to store a reference to a dynamic buffer. You can't do that - any dynamic buffer becomes invalidated by a structural change, so you need to re-get the buffer through the entity. You probably want to store the entity holding the buffer. At least that's my guess based on your code.

    Also I'd suggest you take a look at the ECS Samples to get a better feel for how to write proper unity ECS code. There's a lot of odd things going on in the snippet you posted.