Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Enter the 2020.2 Beta Sweepstakes for a chance to win an Oculus Quest 2.
    Dismiss Notice

How to Access Hybrid Data via Chunk Iteration (not [Inject])?

Discussion in 'Data Oriented Technology Stack' started by S_Darkwell, Nov 26, 2018.

  1. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Good afternoon!

    How does one access data produced from "GameObjectEntity" components via chunk iteration?

    The example documents still show using [Inject] to populate ComponentArrays.

    Alas, while I'm gradually learning how to perform certain tasks, I'm still wrapping my head around archetypes and chunks.

    Thank you in advance! Be well!
    - S.
     
  2. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    I don't believe there is a ComponentArray<T> equivalent in chunk iteration. However, i tend to simply grab the chunk's entity array and just use EntityManager.GetComponentObject<T>(entity).
     
    S_Darkwell likes this.
  3. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Below is one of my systems as an example.

    This system uses [Inject] to retrieve GameObject entities with both a PlayerIdEntity MonoBehavior and Text MonoBehavior. It then finds ECS entities with both a PlayerId component and PlayerInput component.

    After the entities have been collected, it looks through the MonoBehavior values, and when it finds a PlayerIdEntity.Value MonoBehavior and PlayerID.Value ECS entity that match, it appends the content of the ECS entity's PlayerInput.Value to that GameObject entity's Text.text value and destroys the ECS entity.

    Code (CSharp):
    1. using JetBrains.Annotations;
    2.  
    3. using System;
    4. using Unity.Burst;
    5. using Unity.Entities;
    6. using Unity.Collections;
    7. using UnityEngine.UI;
    8.  
    9. [UsedImplicitly]
    10. public sealed class PlayerInputToTextSystem : ComponentSystem {
    11.     private ComponentGroup playerInputGroup;
    12.  
    13.     private struct TextData {
    14.         [UsedImplicitly] public Text Text;
    15.         [UsedImplicitly] public PlayerIdEntity PlayerId;
    16.     }
    17.  
    18.  
    19.     protected override void OnCreateManager() {
    20.         playerInputGroup = GetComponentGroup(
    21.             new EntityArchetypeQuery {
    22.                 All = new[] {
    23.                     ComponentType.Create<PlayerId>(),
    24.                     ComponentType.Create<PlayerInput>()
    25.                 },
    26.                 Any = Array.Empty<ComponentType>(),
    27.                 None = Array.Empty<ComponentType>()
    28.             }
    29.         );
    30.     }
    31.  
    32.     [BurstCompile]
    33.     protected override void OnUpdate() {
    34.         var playerInputChunks = playerInputGroup.CreateArchetypeChunkArray(Allocator.TempJob);
    35.  
    36.         var chunkEntityType = GetArchetypeChunkEntityType();
    37.  
    38.         var chunkPlayerIdType = GetArchetypeChunkComponentType<PlayerId>();
    39.         var chunkPlayerInputType = GetArchetypeChunkComponentType<PlayerInput>();
    40.  
    41.         foreach (var entity in GetEntities<TextData>()) {
    42.             foreach (var chunk in playerInputChunks) {
    43.                 var entities = chunk.GetNativeArray(chunkEntityType);
    44.                 var playerIds = chunk.GetNativeArray(chunkPlayerIdType);
    45.                 var playerInputs = chunk.GetNativeArray(chunkPlayerInputType);
    46.  
    47.                 for (var i = 0; i < playerIds.Length; i++) {
    48.                     if (playerIds[i].Value != entity.PlayerId.Value) {
    49.                         continue;
    50.                     }
    51.  
    52.                     entity.Text.text += (char)playerInputs[i].Value;
    53.                     EntityManager.DestroyEntity(entities[i]);
    54.                 }
    55.             }
    56.         }
    57.  
    58.         playerInputChunks.Dispose();
    59.     }
    60. }
    As mentioned in my original post, I'm quite new to chunk iteration, so any helpful suggestions on how to improve my approach are much appreciated.

    Thank you and be well!
    - S.
     
  4. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Thank you, @dartriminis.

    Do you have an example of how 'EntityManager.GetComponentObject<T>(entity)' can be used to find entities with given MonoBehavior components (eg: Text and PlayerIdEntity)?

    Thank you and be well!
    - S.
     
  5. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    As long as it inherits from Component, you should be able to query it:
    Code (CSharp):
    1. GetComponentGroup(new EntityArchetypeQuery
    2.             {
    3.                 Any  = Array.Empty<ComponentType>(),
    4.                 None = Array.Empty<ComponentType>(),
    5.                 All  = new ComponentType[] { typeof(Text) }
    6.             });
     
    S_Darkwell likes this.
  6. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Hello @dartriminis,

    Text inherits from MonoBehavior (it's in UnityEngine.UI). It doesn't throw any exceptions when I find it as you mention above, but when I try to use "GetArchetypeChunkComponentType<Text>()", it throws an exception stating that Text must be a non-nullable type.

    Is there a way to obtain and iterate through components and entities aside from the method that I'm using above that would be more applicable here?

    Thank you in advance! Be well!
    - S.
     
  7. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    Below is an example of how I use chunk iteration with Unity's CharacterController. Notice that I am not using GetArchetypeChunkComponentType with CharacterController. Rather, I am getting the relevant entity and calling EntityManager.GetComponentObject<CharacterController>().

    Code (CSharp):
    1. public class CharacterControllerSystem : ComponentSystem
    2.     {
    3.         private ComponentGroup _componentGroup;
    4.  
    5.         protected override void OnCreateManager()
    6.         {
    7.             _componentGroup = GetComponentGroup(new EntityArchetypeQuery
    8.             {
    9.                 Any  = Array.Empty<ComponentType>(),
    10.                 None = Array.Empty<ComponentType>(),
    11.                 All  = new ComponentType[] { typeof(Rotation), typeof(CharacterMovement), typeof(CharacterSpeed), typeof(CharacterController) }
    12.             });
    13.         }
    14.  
    15.         protected override void OnUpdate()
    16.         {
    17.             var chunks = _componentGroup.CreateArchetypeChunkArray(Allocator.TempJob);
    18.             if (chunks.Length == 0)
    19.             {
    20.                 chunks.Dispose();
    21.                 return;
    22.             }
    23.  
    24.             var entityTypeRO            = GetArchetypeChunkEntityType();
    25.             var rotationTypeRO          = GetArchetypeChunkComponentType<Rotation>(true);
    26.             var characterMovementTypeRO = GetArchetypeChunkComponentType<CharacterMovement>(true);
    27.             var characterSpeedTypeRO    = GetArchetypeChunkComponentType<CharacterSpeed>(true);
    28.  
    29.             var dt = Time.deltaTime;
    30.  
    31.             for (var chunkIndex = 0; chunkIndex < chunks.Length; chunkIndex++)
    32.             {
    33.                 var chunk = chunks[chunkIndex];
    34.  
    35.                 var entities  = chunk.GetNativeArray(entityTypeRO);
    36.                 var rotations = chunk.GetNativeArray(rotationTypeRO);
    37.                 var movements = chunk.GetNativeArray(characterMovementTypeRO);
    38.                 var speeds    = chunk.GetNativeArray(characterSpeedTypeRO);
    39.  
    40.                 for (var i = 0; i < chunk.Count; i++)
    41.                 {
    42.                     var controller = EntityManager.GetComponentObject<CharacterController>(entities[i]);
    43.  
    44.                     var movement = movements[i].Value * speeds[i].Value * dt;
    45.                     var motion   = math.rotate(rotations[i].Value, movement);
    46.  
    47.                     controller.Move(motion);
    48.                 }
    49.             }
    50.  
    51.             chunks.Dispose();
    52.         }
    53.     }
     
    S_Darkwell likes this.
  8. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Hello @dartriminis,

    This is great. Thank you!

    Alas, I won't have a chance to really examine it until later this week--but from briefly looking over it, it seems to be exactly what I'm seeking.

    Thank you again!
    - S.
     
    dartriminis likes this.
  9. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    668
    You can use ComponentGroup.GetComponentArray<>() to get an array of MonoBehaviour components. Calling EntityManager.GetComponentObject() on every entity is slow.
     
  10. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    I don't believe that works with chunk iteration.
     
  11. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    668
    Yes, you're right. It doesn't. For these cases, I'm willing to forego chunk iteration and use GetComponentArray() and GetComponentDataArray() if I really need both struct components and MonoBehaviour components. Hopefully, a new API that works for MonoBehaviour components in chunk iteration would be introduced.
     
    dartriminis likes this.
  12. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,510
    I used GetComponentObject in one of my in development lib so I have done some research about this.
    The indexer/iterator indeed is faster (and "feel faster" since you are using int to access) but it is good to know that GetComponentObject is not so bad as it feel. Entity is actually a key to get the managed data.

    Screenshot 2018-11-29 15.05.55.png

    CA
    1. Baked type index with generic trick.
    2. Getting the correct chunk : if the same chunk with previous access it is almost free (just if and some booleans). If the index goes over to the new chunk, there are heavy calculation to turn the new index to a new chunk which I guess is expensive.
    Screenshot 2018-11-29 15.15.13.png
    3. Inside of line 53 contains one line of code equals to right side's line 50.

    EM
    1. Type create static access cost to get the type index.
    - Assert does not count in real build.
    2. Getting the correct chunk : 2x pointer access + 2x int copy via out (chunk + chunk index) with Entity as an indexer, no searching.
    3. GetManagedObject : 1x pointer access + cast.

    1. CA wins by small margin since accessing static will be on heap but baked local int should be a stack access and so closer? (not sure)
    2.
    EM has consistent 2 int data copy access cost. Entity speed up access like dictionary.
    CA is expensive on chunk boundary change but cheaper on the same chunk. I am not sure how cheaper the if condition compared to what EM did, but I guess my lib should process less enough entities that using chunk iteration with EM gives me better gains from chunk iteration to offset this CA advantage. (skips chunks, NativeArray Burst advantage, etc)
    3. This is a draw.
     
    GliderGuy, pahe, S_Darkwell and 3 others like this.
unityunity