Search Unity

The proper way to modify data from a query

Discussion in 'Data Oriented Technology Stack' started by unity_6GBXNpZ-SphYHw, Aug 12, 2019.

  1. unity_6GBXNpZ-SphYHw

    unity_6GBXNpZ-SphYHw

    Joined:
    Aug 12, 2019
    Posts:
    5
    Hi, I've been experimenting with Unity ECS for a couple of days now, and I've qot a few questions about queries.

    1) Why can't I use Allocator.Temp with EntityQuery.ToComponentDataArray method? I got some verbose exception about how I can't use Temp inside some job this method creates internally. If I can't even use Temp allocation inside a plain unity update, then when can I use it?

    2) This documentation claims, that this is how you typaclly update component data from a query:

    Code (CSharp):
    1.        
    2.         var positions = m_Group.ToComponentDataArray<Position>(Allocator.Temp);
    3.         var displacememnts = m_Group.ToComponentDataArray<Displacement>(Allocator.Temp);
    4.  
    5.         for (int i = 0; i != positions.Length; i++)
    6.             positions[i].Value = positions[i].Value + displacememnts[i].Value;
    7.  
    For me this code does not even compile, because positions.Value in not a variable and can't be on the left side of the assignment. Also this code example uses Allocator.Temp just fine... Am I doing it wrong?

    3) In some other tutorial I saw this code:

    Code (CSharp):
    1.        
    2.         var positions = m_Group.ToComponentDataArray<Position>(Allocator.Temp);
    3.         for (int i = 0; i != positions.Length; i++)
    4.         {
    5.               var position = positions[i];
    6.               position.Value = ...;
    7.               positions[i] = position;
    8.         }
    9.  
    Which did not work for me either. It compiles but components do not change their value.

    4) Finally calling Manager.SetComponentData does work, but I'm not sure whether that's the best approach.

    Here's my code:

    Code (CSharp):
    1.  
    2. public class EcsTest : MonoBehaviour
    3. {
    4.     private EntityManager _manager;
    5.     private EntityQuery _query;
    6.  
    7.     void Start()
    8.     {
    9.         _manager = new World("custom_world").EntityManager;
    10.         var archetype = _manager.CreateArchetype(typeof(Component));
    11.         _manager.CreateEntity(archetype);
    12.         _query = _manager.CreateEntityQuery(ComponentType.ReadWrite<Component>());
    13.     }
    14.  
    15.     void Update()
    16.     {
    17.         //var entity = _query.ToEntityArray(Allocator.Temp);   <- 1) throws exception
    18.         var entity = _query.ToEntityArray(Allocator.TempJob);  
    19.         var data = _query.ToComponentDataArray<Component>(Allocator.TempJob);
    20.         for (int i = 0; i < data.Length; i++)
    21.         {
    22.             var cmp = data[i];
    23.          
    24.             //data[i].Value = data[i].Value + 1;  <- 2) does not compile
    25.             cmp.Value++;
    26.             //data[i] = cmp  <- 3) does not change component value
    27.             _manager.SetComponentData(entity[i], cmp); // <- 4) this works as expected
    28.             Debug.Log(cmp.Value);
    29.         }
    30.         data.Dispose();
    31.         entity.Dispose();
    32.     }
    33. }
    34.  
    35. struct Component : IComponentData
    36. {
    37.     public int Value;
    38. }
    39.  
    I have a single gameobject with this script on the scene. And the script creates a single world with a single entity.
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,560
    1) You can use Temp allocation within a job, if you're passing an allocation to a job though it should be TempJob
    2) This temp is used outside a job so it's fine. As for the code yeah it's bad. Should be something like this.

    Code (CSharp):
    1. for (int i = 0; i != positions.Length; i++)
    2.     positions[i] = new Position {Value = positions[i].Value + displacememnts[i].Value };
    3) ToComponentDataArray creates a copy. After you make changes you need to copy it back with
    CopyFromComponentDataArray

    4) That's fine, not performant but fine but only because ToComponentDataArray makes a copy. Any EntityManager changes invalidates data so if you were iterating a chunk it would break. EntityCommandBuffer (PostUpdateCommands etc) are used to solve this.
     
    unity_6GBXNpZ-SphYHw likes this.
  3. unity_6GBXNpZ-SphYHw

    unity_6GBXNpZ-SphYHw

    Joined:
    Aug 12, 2019
    Posts:
    5
    Aha, CopyFromComponentDataArray is what I was missing, thanks!

    As for fourth point, does changing component data leads to chunk change? I feel like it shouldn't. I mean entity is not removed and archetype does not change.
     
    Last edited: Aug 13, 2019
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    1,560
    On no, primarily just calling EntityManager causes a sync point right then is all and you will run into issues using it in a systems like that however it's totally fine usually in a MonoBehaviour. I do it occasionally.