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

Updating LocalToWorld during FixedUpdate has no effect

Discussion in 'Entity Component System' started by Abbrew, May 18, 2019.

  1. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    I currently have an entity in the following fashion: "Data" entity -> CenterBottom component -> "CenterBottom" entity. The "Data" entity holds all game logic components while components in the style of CenterBottom serve as locatable markers in the game world. I am trying to update the CenterBottom entity's LocalToWorld in one of my jobs. I structured the code so that the system with the job runs during the FixedUpdate loop. Some debugging proves that the LocalToWorld is indeed being updated. However, once the Update event loop takes place, where the Simulation system group runs, a job runs that causes the LocalToWorld to revert to its previous value. An example of debugging output is:
    Before: (1,1)
    After(2,2)
    Before: (2,2)
    After(3,3)
    Before: (3,3)
    After(4,4)
    New frame
    Before: (1,1)
    After(2,2)
    New frame
    Before: (1,1)
    After: (2,2)
    Before: (2,2)
    After: (3,3)
    I suspect the offending job is CopyTransformFromGameObject.
     
  2. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    502
    As I understand it, this is intentional, and documented. The transform systems take the translation, rotation and scale components, or any combination that are present, and convert the values in a LocalToWorld value. They don't care what value was already there. If you update the Transform, Rotation and/or Scale components, then the change will picked up and reflected in LocalToWorld next time the transform system/s run.
     
    Abbrew likes this.
  3. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    Thank you for the answer. How can I update the Transform from ECS? Updating Translation makes it worse since then the LocalToWorld is not updated at all even between FixedUpdates
     
  4. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    502
    So you have your simulation group running in Fixed Update. This is where you want to run your ECS logic in most cases. The you have your presentation group running in Update. (I think p31 moved simulation to update temporarily while they sort something out).

    For most use cases, updating Translation, Rotation and Scale components in the simulation group is probably fine. Access the components just like you access the LocalToWorld component. Try this first and see if you're happy. My understanding is the intent is for the presentation group to run smoothly at 60fps (update) while the sim group runs less often at your fixed update time step. There may or may not be LocalToWorld smoothing involved, I'm not sure on this myself.

    Here are the docs on the transform system which should detail most of this.
    https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/transform_system.html

    Also, the system update order to see where things are happening.
    https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/system_update_order.html
     
  5. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
  6. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    I followed your advice of updating the relevant Translation component, and using the Entity Debugger I see that the Translation component was updated to the correct value. However, its value is not being copied over to LocalToWorld, whose value remained what it was before the job was run. Here is the job:

    Code (CSharp):
    1. private struct CalculateNextMoveJob : IJobForEachWithEntity<CenterBottom, Grounded>
    2.     {
    3.         //[ReadOnly]
    4.         [NativeDisableParallelForRestriction]
    5.         public BufferFromEntity<TraverseWaypointElement> waypointsFromEntity;
    6.         [NativeDisableParallelForRestriction]
    7.         public ComponentDataFromEntity<Translation> translationFromEntity;
    8.  
    9.         public EntityCommandBuffer.Concurrent ecb;
    10.  
    11.         [ReadOnly]
    12.         public CollisionWorld world;
    13.  
    14.         public int topOfWorld;
    15.         public int bottomOfWorld;
    16.         public LayerMask surfaceMask;
    17.  
    18.         //[WriteOnly]
    19.         //public NativeArray<int3> newPositions;
    20.  
    21.         public void Execute(
    22.             Entity entity,
    23.             int index,
    24.             [ReadOnly] ref CenterBottom centerBottom,
    25.             [ReadOnly] ref Grounded grounded
    26.         )
    27.         {
    28.             var waypoints = waypointsFromEntity[entity];
    29.             var waypointsLength = waypoints.Length;
    30.             if(waypointsLength == 0)
    31.             {
    32.                 ecb.RemoveComponent<GroundedStatus>(index, entity);
    33.                 ecb.AddComponent(index, entity, new TraverseSuccessMessage());
    34.                 return;
    35.             }
    36.  
    37.             var speed = grounded.speed;
    38.             var centerBottomEntity = centerBottom.bottom;
    39.             var currentPosition = translationFromEntity[centerBottomEntity].Value;
    40.             Debug.Log("start: " + translationFromEntity[centerBottomEntity].Value);
    41.             var latestWaypoint = waypoints[waypointsLength - 1].waypoint;
    42.             var wayPointDirection = latestWaypoint - currentPosition.xz;
    43.             var waypointAngle = math.atan(wayPointDirection.y / wayPointDirection.x);
    44.             var newX = (int)((math.cos(waypointAngle) * speed) + currentPosition.x);
    45.             var newZ = (int)((math.sin(waypointAngle) * speed) + currentPosition.z);
    46.             var newLocation = new float2(newX, newZ);
    47.             var newY = SurfaceAt(newLocation, topOfWorld,bottomOfWorld,surfaceMask, world).y;
    48.             var newPosition = new float3(newX, newY, newZ);
    49.             var twoDDistance = (int) math.sqrt(
    50.                 math.pow(math.abs(newPosition.x - currentPosition.x), 2) +
    51.                 math.pow(math.abs(newPosition.y - currentPosition.y), 2)
    52.             );
    53.             var hypotenuse = newPosition - currentPosition;
    54.             var unitHypotenuse = hypotenuse.Normalize();
    55.             var scaledUnitHypotenuse = unitHypotenuse * twoDDistance;
    56.             var finalPosition = (scaledUnitHypotenuse + currentPosition);
    57.             var finalPositionGrounded =
    58.                 new float3(
    59.                     finalPosition.x,
    60.                     SurfaceAt(finalPosition.xz, topOfWorld, bottomOfWorld, surfaceMask, world).y,
    61.                     finalPosition.z
    62.                 );
    63.                    
    64.             // check if the mover stepped over the waypoint
    65.             var overWaypoint =
    66.                 CrossedWaypoint(currentPosition.x, finalPosition.x, latestWaypoint.x)
    67.                 || CrossedWaypoint(currentPosition.z, finalPosition.z, latestWaypoint.y);
    68.             if (overWaypoint)
    69.             {
    70.                 waypoints.RemoveAt(waypoints.Length - 1);
    71.                 finalPositionGrounded = SurfaceAt(latestWaypoint, topOfWorld, bottomOfWorld, surfaceMask, world);
    72.             }
    73.             var translation = translationFromEntity[centerBottomEntity];
    74.             translation.Value = finalPositionGrounded;
    75.             translationFromEntity[centerBottomEntity] = translation;
    76.             Debug.Log("end: " + translationFromEntity[centerBottomEntity].Value);
    77.         }
    78.     }
    It basically calculates finalPositionGrounded and sets Translation to that
     
  7. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    Bump? I've searched through the forums and tried the following:
    • Updating after TransformSystemGroup
    • Updating inside TransformSystemGroup and before CopyTransformToGameObject and after CopyTransformFromGameObject
    • Writing only to Translation Component
    • Moving everything from FixedUpdate to SimulationGroup
    None of these worked. Any suggestions on what may be wrong?
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    Whats the archetype of the entity
     
  9. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    The relevant components are:
    • CopyTransform[To/From]GameObject
    • LocalToWorld
    • MeshFilter
    • MeshRenderer
    • Rotation
    • Transform
    • Translation
     
  10. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    Have you tried without CopyTransformFromGameObject beacuse you're probably overriding your transform
     
    Abbrew likes this.
  11. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    That fixed it. I had a component called "DraggableComponent" that adds a CopyTransformFromGameObject so that dragging the GO would update the ECS side, so I removed it. Now the LocalToWorld is updating when Translation updates, but the mesh rendered in MonoBehaviour land isn't moving with LocalToWorld. May I ask how to fix that?
     
  12. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    Do you have a CopyTransformToGameObject component on it?
     
  13. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    Yes. Here's the list of components:
    • RenderMesh
    • CopyTransformToGameObject
    • WorldRenderBounds
    • RenderBounds
    • Rotation
    • Translation
    • LocalToWorld
    I would also like to add that the entity is not spawned; rather, it's already in the scene
     
  14. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    Sorry for wasting y'all's time. The issue was that I had this:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using Unity.Entities;
    4. using Unity.Transforms;
    5.  
    6. [RequiresEntityConversion]
    7. public class CopyTransformToGameObjectComponent : MonoBehaviour , IConvertGameObjectToEntity
    8. {
    9.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    10.     {
    11.         dstManager.AddComponentData(entity, new CopyTransformToGameObject());
    12.     }
    13.  
    14. }
    15. // This is the result of copy, pasting, and modifying boilerplate code
    16. public struct CopyTransformToGameObject : IComponentData
    17. {
    18.  
    19. }
    20.  
    I removed the struct and now the Authoring adds the correct CopyTransformToGameObject