Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Trying to add a component to a native array of components

Discussion in 'Entity Component System' started by RamblingRoot, Jul 16, 2021.

  1. RamblingRoot

    RamblingRoot

    Joined:
    Apr 2, 2020
    Posts:
    24
    Here is my problem. I have an entity query, I use this to generate a native array of a components with ToComponentDataArray<Component>. This all works fine. However, I need to add one Component to this array inserted at the beginning. It can't be included in the entity query and it has to be at the beginning (or the end, really just needs to have a deterministic index that is consistent with each system update).

    Is there a simple/efficient way to do this? I'm trying to avoid copying and duplicating data.

    I've thought that maybe I could create a native array from the entity query using the entityQuery.ToComponentDataArray<Component>, create another native array using the length of the first plus one (to allow for the additional single component at the start of the new native array), and then go through for each entry and populate the new array with the old array and the single additional component as the first entry. However, it seems like this would involve a lot of unnecessary steps. I can't seem to find anything myself, so hoping that someone here might be able to offer some help.

    Some additional details and specifics about my problem:
    I'm developing a 2d grid fluid simulation system for a large world that is broken into chunks (only the chunks around the player are loaded at any given time). However, I'm only updating fluid in the loaded chunks with the fluid system, and part of the fluid system involves checking the "neighbor" entities of a given block (top, bottom, left, right). So this inevitably leads to the system trying to access data from an unloaded block around the edge of the loaded world (these "unloaded" components are not included in the entityquery, and so currently do not have their data values accessible to the system). Currently, I have a solution in place that uses a bool check to make sure the neighbor blocks are loaded first before trying to read any data from them. It works fine, but to increase speed I'm trying to reduce all logic branches (if statements, etc) inside the system. My idea for a solution was to add a "default" component populated with default values to the start of the native array of components that would be referenced in all cases where a neighbor entity is outside the loaded world. I've got everything worked out aside from how to get the default component inside the same array as the others and at a deterministic index (has to be the same index value for each system update, so preferably either the first or last index for example).
     
    Last edited: Jul 16, 2021
  2. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    You can have a different component as a singleton so you will have your default values there and get them by GetSingleton helper function.
     
  3. RamblingRoot

    RamblingRoot

    Joined:
    Apr 2, 2020
    Posts:
    24
    Interesting, haven't learned about singletons yet, thanks for the suggestion, I'll look into it
     
  4. RamblingRoot

    RamblingRoot

    Joined:
    Apr 2, 2020
    Posts:
    24
    Although, it sounds like this would still require a bool check in order to use the GetSingleton function, yes? If so, it won't achieve what I was hoping. I'm trying to use a native array index to access each fluid block component in the loaded world to update, while accessing this native array, I need one additional component at a deterministic index (an index of 0 for example) that stays the same for each system update. This additional fluid block component would contain default values. This would allow me to use this deterministic index to avoid doing a bunch of bool checks inside a complicated job, but it has to be set up as described to work.

    this might be confusing, but I'll try to explain more exactly what I'm doing. I have an array of entities that contains each 2d block in my world grid. attached to these entities, I have a fluid block component with data values required for the fluid sim system. I also have a "neighbors" component that stores the neighbor entities (top, bottom, left, right). Inside the fluid system I use an entity query to create a native array of the fluid block components for the entities that are in the loaded world only. this native array has it's own index values for each component. In order to know which entities are neighbors inside the native array, I run a job that will update another component that temporarily stores the local index of the created native array to each entity. Later inside the fluid update jobs, I use the neighbors component to access the neighbor entity's temporary index component which I then use to reference the correct fluid block component data inside the native array. Currently, this act of using the neighbors component to access the correct neighbor entity's fluid block component requires that the neighbor entity be inside the loaded world, and so I have to do a bool check beforehand. I'm looking for a way to bypass needing to do this bool check for each time I access a neighbor entity's fluid block data just to see if it is inside the loaded world or not.

    My solution is hopefully as follows: set the temporary index component for each grid block entity to 0. begin the fluid sim system update, do an entity query for all fluid blocks that are currently in the loaded world. Create a native array of fluid block data components with the first index (0) containing a fluid block data component with default values and the following indices corresponding to the fluid block component of each entity returned by the entity query (so the size of the native array would be equal to entityquery length + 1). I would then have the first job re-set the temporary index component value to it's corresponding index in the native array. Now as the fluid system updates, when I access a neighbor entity that isn't in the loaded world, it will use the default temporary index of 0 to pull data from the native array, this would pull the default fluid component data at the index of 0. The loaded entites would return a tempory index value != 0, accessing the respective entity's fluid block component data and not the default fluid component data. So this would allow me to remove all bool checks from my current logic. And finally, at the end of the fluid system update I would return the temporary index values that were reset to equal the default value of zero. So at the start of the fluid system and after it has finished, all entities would have a temporary index component value of 0 (the default index), ready for the next fluid system update.

    Hopefully this makes sense. Not sure if using a singleton would allow this or not

    TLDR: my problem is creating a native array that contains one component of default data values at 0 index followed by the components of each entity from an entityquery. I can't seem to find an easy way to do this
     
    Last edited: Jul 17, 2021
  5. RamblingRoot

    RamblingRoot

    Joined:
    Apr 2, 2020
    Posts:
    24
    The more I've played around with this idea, the more it seems like it just won't work like I had hoped. May have to table this optimization effort and just accept that bool checks are required, at least for now.
     
  6. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,223
    Don't use the EntityQuery API to create your array. Use EntityQuery.CalculateEntityCount() to size your array appropriately and use Entities.ForEach with entityInQueryIndex to write out your data to the array, offsetting the index by one.
     
  7. RamblingRoot

    RamblingRoot

    Joined:
    Apr 2, 2020
    Posts:
    24
    That's an interesting idea, unfortunately, I don't think I can use entities.foreach, or at least it doesn't seem like it would make sense to. But I could be wrong.

    I'm doing several steps of the fluid sim update as separate jobs in the overall fluid system. certain things have to be completely updated before moving to the next step. If done with entities.foreach, I'd have to update all the entity values at each step as opposed to native arrays of individual components. Also, my entire idea of using a default component struct stored at a known index of a native array really only works with a native array (I think). If I used entities.foreach instead, I'd have to instead have a default entity storing the default component struct, and I'd also have to store and update a default pointer values for every entity in the world grid, every time entities are loaded or unloaded, I'd have to check and switch the entity pointers in its "neighbors" component to point to the default entity instead of its actual neighbors, and only if the entity is one of the border grid blocks of a given chunk of the world (I load and unload the world in chunks that populate around the player). So this sounds like it could be as expensive as a few math.select functions that I'm currently using. But maybe I'll try it out at some point to test.

    In any case, turns out there were other areas where I found some significant speed gains, so for now I'm satisfied with the speed of the system.