Search Unity

Efficient approach for checking a neighbouring grid?

Discussion in 'Entity Component System' started by Defiant_Games, Apr 2, 2018.

  1. Defiant_Games

    Defiant_Games

    Joined:
    Jul 18, 2012
    Posts:
    6
    Loving the direction Unity is going with ECS and the top shelf talent they've brought in.

    Have a scenario maybe others have also come across and looking for an efficient way to go about it in Unity ECS. While I can do it fairly easily in my own rolled ECS system, I would love to transfer to Unity ECS.

    Setup: Grid based game. GridIndex = (Z * cells across) + X
    Desired action: Unit wants to move into neighboring grid.
    Problem: Must check if new grid is occupied first.


    Current thinking

    GridID:IComponentData { int GridIndex }
    GridOccupation:IComponentData { }

    Option 1:
    -> Iterate all entities with GridID and GridOccupation to find a GridIndex that matches the neighbor.
    -> If find a match, grid is blocked, if not then grid is available to move into.

    Option 2:
    -> Maintain a lookup table: Dictionary<GridIndex, Entity>
    -> Get neighboring entity from lookup table.
    -> Check if GridOccupation data exists on neighboring entity. If not then grid is available to move into.

    Option 3:
    ...the obvious thing I'm not thinking of?

    Potentially doing this constantly on a very large scale. Option 1 feels more true to ECS and data orientated design but every unit would have to iterate over a lot of GridID data just to find if its neighbor is blocked. Feel like I may be overlooking something obvious? Is there any internal lookup table in the ECS or a fast way to filter results with specified component data values? e.g. Get GridOccupation data for entities with GridID.GridIndex == 123

    If I did go with maintaining a <GridIndex, Entity> LookupTable, is storing Entities as the value in a Dictionary safe? They don't look to have any ID other than the struct itself.
     
  2. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    What is the relation of your grid with ECS? It's not clear
     
  3. Defiant_Games

    Defiant_Games

    Joined:
    Jul 18, 2012
    Posts:
    6
    Oh, apologies.

    My thinking was when a grid cell is Occupied, I would create a new entity to represent that Occupation. The entity would have 2 data components: a GridIndex data component and a flag/marker data component to show the grid is occupied.
     
  4. StephanK

    StephanK

    Joined:
    Jul 10, 2012
    Posts:
    47
    I also had this problem, what I did for now is creating a an array that maps the entity index to the ComponentDataArray index. And then iterate over that map and accessing all ComponentDataArrays with map.

    Code (CSharp):
    1. // entities are one-based
    2. var map = new int[g.Length + 1];      
    3. for (int i = 0; i < g.entities.Length; i++) {
    4.     map[g.entities[i].Index] = i;
    5. }
    In my case that seems to be ok, since the grid is not that big (~100 cells). The definitive downside is that to be safe you have to do it on each update of the system, since you don't know when the order has changed. I hope there is some kind of hook/event or at least some hint that i can exploit to recreate the map only when i have to.
     
    andywatts likes this.
  5. StephanK

    StephanK

    Joined:
    Jul 10, 2012
    Posts:
    47
    Hm I experimented a bit and I thought I could just check Entity.Version, but unfortunately that seems to be always 1. But what seems to work is to check
    EntityManager.GetComponentOrderVersion<T>(). That number seems to change everytime the array is reordered. It's too late to check the source what it's actually doing though. Maybe someone from uniy can shed a light on the internals here? ;) maybe @superpig ?
     
  6. mike_acton

    mike_acton

    Unity Technologies

    Joined:
    Nov 21, 2017
    Posts:
    110
    > EntityManager.GetComponentOrderVersion<T>(). That number seems to change everytime the array is reordered.
    Anything that would change the order of the components will bump the version number, intentionally. So any adds or removes of this component type across all archetypes it's on.

    But if your board is stable, you can add a shared component. Something like:
    Code (CSharp):
    1. [Serializable]
    2. struct BoardId : ISharedComponentData
    3. {
    4.   int Value;
    5. }
    Then you can use:
    Code (CSharp):
    1. EntityManager.GetSharedComponentOrderVersion<T>(T sharedComponent)
    Which has the same rules as ComponentOrder but restricted to just the components that share a particular shared component. (So the version will be stable except when you add/remove components/entities that share that specific value representing your board.)
     
    bobbaluba likes this.
  7. StephanK

    StephanK

    Joined:
    Jul 10, 2012
    Posts:
    47
    Makes sense. Thanks. What is the purpose of Entity.Version?
     
  8. Malmer

    Malmer

    Joined:
    Nov 10, 2013
    Posts:
    18
    The lifetime validity of the Entity struct isn't really clear to me. Consider this scenario.

    Step 1
    In one component system I build an array-grid that contains Entity. For the construction of the array I use a ComponentDataArray that references the component data tag "AllMyPrettyItems".

    Step 2
    Now later in the frame I have a system that runs EntityManager.AddComponent and adds the ComponentData "Foo" to the subset of entities that have both the tag "AllMyPrettyItems" and "Bar" (injected as a componentgroup).

    Step 3
    Even later in the frame the Entity stored away in the array grid in step 1 is used, maybe using EntityManager.SetComponentData. Is that entity still valid, or would I need to regenerate my array-grid once more after the changes made in Step 2? And if it is valid in this case, will it get invalid in the next frame, requiring the system in step 1 to make sure it is up to date?
     
    Last edited: Apr 4, 2018
  9. mike_acton

    mike_acton

    Unity Technologies

    Joined:
    Nov 21, 2017
    Posts:
    110
    > The lifetime validity of the Entity struct isn't really clear to me. Consider this scenario.

    Entity ids are stable. For example:
    Code (CSharp):
    1.     public struct TransformParent : IComponentData
    2.     {
    3.         public Entity Value;
    4.     }
    The index in a ComponentDataArray is only stable if no changes have been made that would affect the archetypes that include them (add/remove component) or entities they are associated with (add/delete entity) - so unless you're specifically checking for that (via the Version), you would store Entity and not index in order to look up later.


    ComponentDataFromEntity<> allows you to look up component by Entity as index for that purpose.

    > What is the purpose of Entity.Version?

    To check if you're referring to an Entity that was previously deleted.
     
    Malmer likes this.