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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Write to referenced Entity's component data

Discussion in 'Entity Component System' started by timothy_j, Jun 7, 2020.

  1. timothy_j

    timothy_j

    Joined:
    Apr 11, 2020
    Posts:
    12
    I have an Entity reference in a component and I'm trying write new values to one of the referenced entity's components. However, I just don't seem to be able to achieve it. I'm sure I'm just missing something obvious.
    I have the following components
    ...on the 'wheel':
    Code (CSharp):
    1. public struct TorqueControl : IComponentData
    2. {
    3.     public float AppliedTorque;
    4.     public float3 Axis;
    5. }
    ...and on the player entity
    Code (CSharp):
    1. public struct BallMoveControl : IComponentData
    2. {
    3.     public Entity BallEntity;
    4.     public float Torque;
    5.     public float BoostTorque;
    6. }
    The idea is that the following system uses the Entity reference in the BallMoveControl to update the data in the TorqueControl (and no, the logic is not complete yet):
    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Entities;
    3. using Unity.Mathematics;
    4. using UnityEngine;
    5.  
    6. [UpdateBefore(typeof(ApplyTorqueSystem))]
    7. public class PlayerMoveSystem : SystemBase
    8. {
    9.     Entity cameraEntity;
    10.  
    11.     [NativeDisableParallelForRestriction]
    12.     public ComponentDataFromEntity<TorqueControl> tcGroup;
    13.  
    14.     protected override void OnStartRunning()
    15.     {
    16.         Entities.WithAll<Camera, Transform>().WithoutBurst().ForEach((Entity entity) =>
    17.         {
    18.             cameraEntity = entity;
    19.         }).Run();
    20.     }
    21.  
    22.     protected override void OnUpdate()
    23.     {
    24.         Transform trans = EntityManager.GetComponentObject<Transform>(cameraEntity);
    25.         quaternion camRotation = new quaternion();
    26.         camRotation = math.quaternion(trans.rotation.x, trans.rotation.y, trans.rotation.z, trans.rotation.w);
    27.         float vAxis = Input.GetAxis("Vertical");
    28.         float hAxis = Input.GetAxis("Horizontal");
    29.         bool sprint = Input.GetButton("Sprint");
    30.  
    31.         tcGroup = GetComponentDataFromEntity<TorqueControl>();
    32.  
    33.         Entities.WithAll<IsPlayerTag>().WithoutBurst().ForEach((ref BallMoveControl bmc) =>
    34.         {
    35.             if (vAxis != 0 || hAxis != 0)
    36.             {
    37.                 // create force vector (in camera space)
    38.                 float3 axis = new float3(vAxis, 0f, -hAxis);
    39.  
    40.                 // transform into world space
    41.                 axis = math.rotate(camRotation, axis);
    42.  
    43.                 // Remove y component
    44.                 axis.y = 0;
    45.  
    46.                 if (tcGroup.Exists(bmc.BallEntity))
    47.                 {
    48.                     tcGroup[bmc.BallEntity].Axis = axis;
    49.                     tcGroup[bmc.BallEntity].AppliedTorque = sprint ? bmc.BoostTorque : bmc.Torque;
    50.  
    51.                     // Using the following in debug shows that the correct component data is being retrieved
    52.                     // but it is obviously being copied and not written back to the source:
    53.  
    54.                     //TorqueControl localTC = tcGroup[bmc.BallEntity];
    55.                     //localTC.Axis = axis;
    56.                     //localTC.AppliedTorque = sprint ? bmc.BoostTorque : bmc.Torque;
    57.                 }
    58.             }
    59.         }).Run();
    60.  
    61.     }
    62. }
    As it stands this won't even compile as "the return value of tcGroup[bmc.BallEntity] can't be modified because it is not a variable". This is obviously why using the commented code doesn't work, even though it runs.

    What am I doing wrong or am I taking the wrong approach completely?
     
  2. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    677
    Make the tcGroup local and add WithNativeDisableParallelForRestriction(tcGroup) to your Entities call.

    EDIT: Re-reading it I noticed that you are using Run(), in this case I don't think you need the WithNativeDisableParallelForRestriction(tcGroup) at all.

    EDIT 2: Noticed also that you are not writing back to the tcGroup:
    Code (CSharp):
    1. TorqueControl localTC = tcGroup[bmc.BallEntity];
    2. localTC.Axis = axis;
    3. localTC.AppliedTorque = sprint ? bmc.BoostTorque : bmc.Torque;
    4. tcGroup[bmc.BallEntity] = localTC;
     
    timothy_j likes this.
  3. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,993
    SystemBase has GetComponent and SetComponent methods which let you not worry about ComponentDataFromEntity.
     
    timothy_j likes this.
  4. timothy_j

    timothy_j

    Joined:
    Apr 11, 2020
    Posts:
    12
    Thanks! My main mistake was sorted by your Edit 2 - I wasn't writing the values back. I really ought to brush up on my 'by reference' vs 'by value'!
    As for your first comment, I was always intending to use ScheduleParallel (even if it is not really required for this system) - I just switched to Run() to make sure things weren't being processed out of order somehow.

    Is there an alternative to WithNativeDisableParallelForRestriction for using SetComponent in a ForEach with ScheduleParallel? If so, it looks like that would give the cleanest code. Otherwise this is what I ended up with:
    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Mathematics;
    3. using UnityEngine;
    4.  
    5. [UpdateBefore(typeof(ApplyTorqueSystem))]
    6. public class PlayerMoveSystem : SystemBase
    7. {
    8.     Entity cameraEntity;
    9.  
    10.     protected override void OnStartRunning()
    11.     {
    12.         Entities.WithAll<Camera, Transform>().WithoutBurst().ForEach((Entity entity) =>
    13.         {
    14.             cameraEntity = entity;
    15.         }).Run();
    16.     }
    17.  
    18.     protected override void OnUpdate()
    19.     {
    20.         Transform trans = EntityManager.GetComponentObject<Transform>(cameraEntity);
    21.         quaternion camRotation = new quaternion();
    22.         camRotation = math.quaternion(trans.rotation.x, trans.rotation.y, trans.rotation.z, trans.rotation.w);
    23.         float vAxis = Input.GetAxis("Vertical");
    24.         float hAxis = Input.GetAxis("Horizontal");
    25.         bool sprint = Input.GetButton("Sprint");
    26.  
    27.         ComponentDataFromEntity<TorqueControl> tcGroup = GetComponentDataFromEntity<TorqueControl>();
    28.  
    29.         Entities.WithAll<IsPlayerTag>().WithNativeDisableParallelForRestriction(tcGroup).ForEach((ref BallMoveControl bmc) =>
    30.         {
    31.             if (vAxis != 0 || hAxis != 0)
    32.             {
    33.                 // create force vector (in camera space)
    34.                 float3 axis = new float3(vAxis, 0f, -hAxis);
    35.  
    36.                 // transform into world space
    37.                 axis = math.rotate(camRotation, axis);
    38.  
    39.                 // Remove y component
    40.                 axis.y = 0;
    41.  
    42.                 if (tcGroup.Exists(bmc.BallEntity))
    43.                 {
    44.                     TorqueControl localTC = tcGroup[bmc.BallEntity];
    45.                     localTC.Axis = axis;
    46.                     localTC.AppliedTorque = sprint ? bmc.BoostTorque : bmc.Torque;
    47.                     tcGroup[bmc.BallEntity] = localTC;
    48.                 }
    49.             }
    50.             else
    51.             {
    52.                 // I don't like the way this runs all the time when there isn't player input, but it has to be reset somehow!
    53.                 if (tcGroup.Exists(bmc.BallEntity))
    54.                 {
    55.                     TorqueControl localTC;
    56.                     localTC.Axis = new float3(0f, 0f, 0f);
    57.                     localTC.AppliedTorque = 0f;
    58.                     tcGroup[bmc.BallEntity] = localTC;
    59.                 }
    60.             }
    61.         }).ScheduleParallel();
    62.  
    63.     }
    64. }
     
  5. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,993
    Unless you have thousands of entities with the IsPlayerTag component, ScheduleParallel seems completely unnecessary. Just use Schedule and then you can use SetComponent just fine.