Search Unity

Reconciling Translation based on RigidBody position

Discussion in 'Entity Component System' started by LukePammant, Apr 30, 2019.

  1. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    tldr; How can I get the Translation from an entity after the Rigidbody calculations?

    I'm having trouble figuring out how to sync the `Translation` component on Rigidbody based character controller.

    I can get `PlayerMovementSystem` to update the player's position via `rigidBody.MovePosition(newPos);` but if the new position is colliding with something I need to get the "correct" position back into the Entity's `Translation` component.

    Do I need to create another System that copies the Transform location back into the Entity after FixedUpdate or something?

    Here is my simple movement system:
    Code (CSharp):
    1. [UpdateAfter(typeof(MovementSpeedCalculationSystem))]
    2. public class PlayerInputMovementSystem : ComponentSystem
    3. {
    4.     protected override void OnUpdate()
    5.     {
    6.         if (Input.GetAxis("Horizontal") == 0 && Input.GetAxis("Vertical") == 0)
    7.             return;
    8.  
    9.         var horizontal = Input.GetAxis("Horizontal");
    10.         var vertical = Input.GetAxis("Vertical");
    11.         var sprint = Input.GetButton("Sprint");
    12.  
    13.         Entities.WithAll<PlayerTag>().ForEach((Rigidbody rigidBody, ref MoveSpeed moveSpeed, ref Translation position) =>
    14.         {
    15.             var direction = math.normalize(new float3(horizontal, 0, vertical));
    16.             var movement = direction * moveSpeed.CalculatedSpeed * Time.deltaTime;
    17.             movement *= sprint ? 2f : 1f;
    18.             var newPosition = position.Value + movement;
    19.             rigidBody.MovePosition(newPosition);
    20.             position.Value = newPosition; // This is incorrect and causes the Translation to be in the middle of a wall
    21.         });
    22.     }
    23. }
     
    Last edited: Apr 30, 2019
  2. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    This is a seemingly simple thing so perhaps I'm asking the wrong question or doing it the wrong way. (How)Do you use Rigidbody's with your entities for movement? Or do you do some sort of manual ray casting to determine where an object should be after it's moved?
     
  3. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    Does this work the same as it does for Rigidbody2D? If so, the docs say that MovePosition just computes the forces required to get it to its new position, but those forces aren't applied until the physics update. So its final position isn't going to equal newPosition if there's a wall in the way.
    If that's the case, you need to run a system after FixedUpdate which copies the positions from your GameObjects to your Translations.
    This is how I do it in my game using Rigidbody2D though I'm not using MovePosition.
     
  4. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    Awesome - that worked thank you. I didn't realize you could constrain systems to run after FixedUpdate. Looks like it's an experimental feature?

    Here is my code for anyone interested in the future:
    Code (CSharp):
    1. public struct CopyTransformPositionToTranslation : IComponentData { }
    2.  
    3. [UpdateAfter(typeof(UnityEngine.Experimental.PlayerLoop.FixedUpdate))]
    4. public class CopyTransformPositionToTranslationSystem : ComponentSystem
    5. {
    6.     protected override void OnUpdate()
    7.     {
    8.         Entities.WithAll<CopyTransformPositionToTranslation>().ForEach(
    9.             (Transform transform, ref Translation translation) => translation.Value = transform.position
    10.         );
    11.     }
    12. }
    13.  
     
  5. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    I think that is basically circumventing the rigidbody movement, if you have things setup so changes to the position are also copied to the gameobject. Running this after fixed update, you are calling MovePosition but the transform position will get set from your position assignment before the next physics update which applies the result of MovePosition.

    For physics I would manually tick your systems in fixed update and treat the position in DOTS as readonly.
     
    LukePammant likes this.
  6. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    That PlayerLoop.FixedUpdate was how I was doing it before they created the Simulation and Presentation groups but now I just run the whole SimulationGroup from within FixedUpdate with my CopyTranslation system being one of the first to run.
    You just have to be careful about sync cos your FixedUpdate runs at a different frequency to SimulationGroup.
    So long as it works though.

    - Edit -
    Oh yes and you should get rid of that position.Value = newPosition;
     
    LukePammant likes this.
  7. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    Does `[UpdateAfter(typeof(UnityEngine.Experimental.PlayerLoop.FixedUpdate))]` essentially do that? Or is there a better/more explicit way to do so?
     
  8. JooleanLogic

    JooleanLogic

    Joined:
    Mar 1, 2018
    Posts:
    447
    Yes what Snacktime said. So long as the only place you directly set the Translation for rigid bodies is in CopyTransformPositionToTranslation, then it's fine cos the Translation will always match the position of the rigid body.

    If you're using physics though, you should think about what else needs to happen in there because if your physics is running at 50fps and your framerate is 60 or higher, then you could be running systems more often than you need to and wasting cycles.
     
    LukePammant likes this.