Search Unity

Overriding object Transform every frame for multi-coordinate system stops Unity physics

Discussion in 'Physics' started by IanNicolades, Sep 28, 2018.

  1. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    Hi there, I've already asked this question on the Answers section but did not receive any responses and it seemed to have gotten lost in the shuffle pretty quickly. I also emailed the Unity support team but have yet to hear anything back. Apologies if cross-posting this breaks any rules - I'm simply at a loss as to how to proceed.

    I have a very complex simulation in a large scale spherical terrain environment that has been developed in Monogame. We are in the process of evaluating Unity for integration with it. Consider it an enormous black box that uses its own coordinate system - you provide game objects to it as input, it transforms their positions and returns them for rendering.

    What we need is the ability to intercept and override the Transform of every gameobject that is used by our system, but only for rendering - keep its Transform exactly the same, in Unity 0,0,0-based coordinate space for updates, physics, that sort of thing, and before it is used for rendering, replace it with the transformed Transform generated by our system. Then, seamlessly put it back into Unity space for physics and such to continue as normal. This allows us to utilize two coordinate spaces at once, which is a critical feature.

    With the assistance of the unity channel on a large game dev discord, I've been able to prototype almost all of the way there. Currently, I have a script attached to every gameobject that does this:

    Code (CSharp):
    1.      bool haveTransform = false;
    2.      Vector3 tmpPosition = Vector3.zero;
    3.      Quaternion tmpRotation = Quaternion.identity;
    4.      Vector3 tmpScale = Vector3.zero;
    5.    
    6.      private void OnEnable()
    7.      {
    8.          FirstScript.PreUpdate += HandlePreUpdate;
    9.      }
    10.    
    11.      void HandlePreUpdate()
    12.      {
    13.          if (haveTransform)
    14.          {
    15.              this.transform.position = tmpPosition;
    16.              this.transform.rotation = tmpRotation;
    17.              this.transform.localScale = tmpScale;
    18.          }
    19.      }
    20.    
    21.      private void LateUpdate()
    22.      {
    23.          tmpPosition = this.transform.position;
    24.          tmpRotation = this.transform.rotation;
    25.          tmpScale = this.transform.localScale;
    26.          haveTransform = true;
    27.    
    28.          this.transform.position = ComputeRenderedTransformPosition();
    29.          ...
    30.      }
    I followed the steps in this question https://answers.unity.com/questions/614343/how-to-implement-preupdate-function.html to implement the PreUpdate method - I also attached the FirstScript to an object in the scene so that the event would be fired in game.

    And, this works great! Objects are properly translated into our coordinate system for rendering, woohoo! :D

    The issue arises when attempting to support Unity's physics. If I set up a test scene with a few cubes on top of a quad, and do not enable that script, thus leaving them completely in Unity's coordinate space, then it naturally works as expected - the cubes tumble playfully to the ground with lovely physics and all that. However, once I enable the script, they simply freeze in space - no physics are applied, nothing.

    I am assuming that the issue is that the physics engine detects that the Transform was modified that frame, assumes that it was repositioned, and skips updating their physics that frame. Since this is not the case, I need to find a way to suppress this behavior.

    Someone on the Discord suggested that I try calling Physics.SyncTransforms() each frame - I tried doing so in a few different places, but it doesn't seem to have any effect. Any further help would be greatly appreciated!
     
  2. jvggp

    jvggp

    Joined:
    Jul 8, 2018
    Posts:
    30
    Do you have your game Objects static?
    First don't use transform for set, only for get. So read but not write. Give them a rigidbody and set it to kinematic. It's enough when the parent gameobject has a rigidbody. Then in the script:

    //transform.position = tmpPosition; don't use this instead:
    GetComponentInParent<Rigidbody>().position=tmpPosition;

    This might also solve your phyisics is not working problem. A physic object needs a rigidbody. Else no physics ;)
     
  3. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    The objects are not static. As I said, if I turn off the coordinate swapping script, physics work perfectly - all I need is for them to continue working perfectly when I swap coordinates for rendering. :D

    The objects already have rigid bodies attached - I tried setting its position and rotation as you suggested, but it seems to just exacerbate the issue, as while they do move, they now jitter uncontrollably in completely inaccurate positions.
     
  4. jvggp

    jvggp

    Joined:
    Jul 8, 2018
    Posts:
    30
    Ok, for me it works. But you should use Rigidbody to move, it's recommended by unity. Transform shouldn't be use because of performance. And you override the tmpPosition in your ComputeRenderedTransformPosition()? if not it's clear it doesn't work. Further i don't know.
     
  5. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    This does not work for me.

    I have created a reproduction of the issue in a completely separate script to illustrate the behavior that I am seeing vs. what I am expecting, using both the rigid body method suggested and the transform method that I was originally experimenting with. Note that this is extremely simplified - the real system naturally has far more complex coordinate mapping systems, but as it simply functions as a black box of receiving input in one coordinate system and providing output for rendering in a different coordinate system, this is an accurate representation of its functionality for the purposes of testing.

    Prerequisite: the PreUpdate script from this thread https://answers.unity.com/questions/614343/how-to-implement-preupdate-function.html - ensure those directions are followed so the script update time is setup properly, and then attach it to the camera or another object in the scene so that it is run with the game.

    Test coordinate mapping script:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class CoordinateMapper : MonoBehaviour {
    6.  
    7.     private void OnEnable()
    8.     {
    9.         FirstScript.PreUpdate += HandlePreUpdate;
    10.     }
    11.  
    12.     private void OnDisable()
    13.     {
    14.         FirstScript.PreUpdate -= HandlePreUpdate;
    15.     }
    16.  
    17.     bool haveTransform = false;
    18.     Vector3 tmpPosition = Vector3.zero;
    19.     Quaternion tmpRotation = Quaternion.identity;
    20.     Vector3 tmpScale = Vector3.zero;
    21.  
    22.     Vector3 secondCoordinatePosition = new Vector3(100, 100, 100);
    23.     Vector3 secondCoordinateScale = new Vector3(10, 10, 10);
    24.     Quaternion secondCoordinateRotation = Quaternion.Euler(90, 0, 0);
    25.  
    26.     bool useRigidBody = false;
    27.  
    28.     void HandlePreUpdate()
    29.     {
    30.         if (haveTransform)
    31.         {
    32.             this.transform.localScale = tmpScale;
    33.             //test two different ways of setting position/rotation, only one way to set scale?
    34.             if (useRigidBody)
    35.             {
    36.                 GetComponentInParent<Rigidbody>().position = tmpPosition;
    37.                 GetComponentInParent<Rigidbody>().rotation = tmpRotation;
    38.             }
    39.             else
    40.             {
    41.                 this.transform.position = tmpPosition;
    42.                 this.transform.rotation = tmpRotation;
    43.             }
    44.         }
    45.     }
    46.  
    47.     private void LateUpdate()
    48.     {
    49.         if (useRigidBody)
    50.         {
    51.             tmpPosition = GetComponentInParent<Rigidbody>().position;
    52.             tmpRotation = GetComponentInParent<Rigidbody>().rotation;
    53.         }
    54.         else
    55.         {
    56.             tmpPosition = this.transform.position;
    57.             tmpRotation = this.transform.rotation;
    58.         }
    59.         tmpScale = this.transform.localScale;
    60.         haveTransform = true;
    61.  
    62.         if (useRigidBody)
    63.         {
    64.             GetComponentInParent<Rigidbody>().position = secondCoordinatePosition + tmpPosition;
    65.             GetComponentInParent<Rigidbody>().rotation = secondCoordinateRotation;
    66.         }
    67.         else
    68.         {
    69.             this.transform.position = secondCoordinatePosition + tmpPosition;
    70.             this.transform.rotation = secondCoordinateRotation;
    71.         }
    72.         this.transform.localScale = secondCoordinateScale;
    73.     }
    74.  
    75.     // Use this for initialization
    76.     void Start () {
    77.        
    78.     }
    79.    
    80.     // Update is called once per frame
    81.     void Update () {
    82.        
    83.     }
    84. }
    85.  
    Set up a simple test scene with a few cubes, each with rigid body physics enabled on them.

    Scenario 1: no scripts enabled. Expected behavior: the cubes should fall and tumble with physics as expected. This works as expected.

    Scenario 2: CoordinateMapper script attached to each object as detailed above, useRigidBody flag set to true. Expected behavior: the cubes should fall and tumble with physics, but be rendered at an offset position, rotation, and scale. Resulting behavior: all of the objects simple freeze in place, unaffected by physics.

    Scenario 3: same as 2, useRigidBody flag set to true, as suggested in this thread. Expected behavior: the cubes should fall and tumble with physics, but be rendered at an offset position, rotation and scale. Resulting behavior: all of the objects seem to jitter uncontrollably.

    Performance is not an issue at the moment. I need to prototype this functionality first in order to confirm whether or not Unity will be a good fit for our technology before we move forward with development.
     
  6. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    I am unable to edit my post above, but to fix a typo: in scenario 2, the useRigidBody flag should be set to false.
     
  7. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    I do this in my project. You need to set the transformations in FixedUpdate to.

    To know the position you need to set, create an AfterFixedUpdate that takes the values of the previous physical simulation. You can do this by marking a dirty variable in FixedUpdate and checking for it in Update.
     
  8. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    I've tried it in FixedUpdate as well. I've also tried setting a flag in FixedUpdate and then setting the transforms in Update if the flag was set to true. As you can see in the reproduction case that I have attached in my previous set of posts, it simply doesn't work for me.

    davidfrk, could you elaborate upon the manner in which you are doing this and see where it might differ from the repro case I posted?
     
  9. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    What I do is similar to this, check if this helps.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. //Set this script to run first in script execution order
    6. public class VisualPositionTransform : MonoBehaviour
    7. {
    8.     bool afterFixedUpdate = false;
    9.     Vector3 lastFixedUpdatePosition;
    10.     Quaternion lastFixedUpdateRotation;
    11.  
    12.     Vector3 lastUpdatePosition;
    13.     Quaternion lastUpdateRotation;
    14.  
    15.     public Vector3 testPositionOffset;
    16.     public Vector3 testRotationOffset;
    17.     public Transform debugPhysicsTransform;
    18.  
    19.     void Start()
    20.     {
    21.         lastFixedUpdatePosition = transform.position;
    22.         lastFixedUpdateRotation = transform.rotation;
    23.         lastUpdatePosition = transform.position;
    24.         lastUpdateRotation = transform.rotation;
    25.     }
    26.  
    27.     void FixedUpdate()
    28.     {
    29.         transform.position = lastFixedUpdatePosition;
    30.         transform.rotation = lastFixedUpdateRotation;
    31.         afterFixedUpdate = true;
    32.     }
    33.  
    34.     void Update()
    35.     {
    36.         if (afterFixedUpdate)
    37.         {
    38.             lastFixedUpdatePosition = transform.position;
    39.             lastFixedUpdateRotation = transform.rotation;
    40.             afterFixedUpdate = false;
    41.         }
    42.  
    43.         VisualUpdate();
    44.     }
    45.  
    46.     //This part does not need to run before other scripts
    47.     void VisualUpdate()
    48.     {
    49.         //Set visualPosition, whatever you want
    50.         transform.position = testPositionOffset + lastFixedUpdatePosition;
    51.         transform.rotation = Quaternion.Euler(testRotationOffset) * lastFixedUpdateRotation;
    52.  
    53.         //Storing this value in the case you want to use the update positions
    54.         lastUpdatePosition = transform.position;
    55.         lastUpdateRotation = transform.rotation;
    56.  
    57.         if (debugPhysicsTransform != null)
    58.         {
    59.             debugPhysicsTransform.position = lastFixedUpdatePosition;
    60.             debugPhysicsTransform.rotation = lastFixedUpdateRotation;
    61.         }
    62.     }
    63. }
    64.  
    Video
     
    Last edited: Sep 30, 2018
  10. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    davidfrk, thank you very much for your detailed response - I'm not entirely certain what the differences are to Unity between your script and mine, but yours does indeed work. Fantastic!

    And now for the super extra bonus round question...

    In a more complex test scene using UMA characters and the Unity third person camera control, everything works great with this system except that said camera control appears to be using the incorrect camera transform during its update. I have triple checked that the script ordering is correct, and am not sure what else could be the issue - both the camera object and the other scene objects have the script attached, and outputting debug text at each update step seems to indicate that they are being ordered properly, so I'm not sure how to track down the issue.

    - FixedUpdate() occurs first in the visual position transform script, which should set all scene objects to Unity's coordinate system.
    - The ThirdPersonUserControl's Update or FixedUpdate (I've tested both) occurs, but is somehow getting the wrong coordinates. I'm not sure if this is due to how it's getting the camera's Transform (by setting a reference from Camera.main.transform), perhaps.
    - The visual position transform's LateUpdate method is called, which should set all scene objects to the rendering coordinates.

    I tried calling Physics.SyncTransforms() in the visual position transform script's FixedUpdate(), but that did not seem to have any effect either.
     
  11. jvggp

    jvggp

    Joined:
    Jul 8, 2018
    Posts:
    30
    I tried your code. First is gitters because between Update() and LateUpdate() there is no rendering. And the object fly away because you add the same value at each update.
    I tried other Functions but don't get it to work properly. Here https://docs.unity3d.com/Manual/ExecutionOrder.html you can see the lifetime cycle. You will see why Update() and LateUpdate() can't work.
    There are also the function calls of the Rendering process. But some are just called in camera other in object :/

    What you can do is that you have two objects. One just has the pysics and the other is for rendering. This works. Attach this script to the object which has no rendering. The RenderGameObject is the gameobject which is renderd.:

    Code (CSharp):
    1.  Vector3 secondCoordinatePosition = new Vector3(5f, 0f, 5f);
    2.     Vector3 secondCoordinateScale = new Vector3(.5f, .5f, .5f);
    3.     Quaternion secondCoordinateRotation = Quaternion.Euler(45f, 45f, 45f);
    4.  
    5.     public GameObject RenderGameObject;
    6.  
    7.    
    8.     public bool doMaping = true;
    9.    
    10.  
    11.     void Update()
    12.     {
    13.        
    14.         if (doMaping)
    15.         {
    16.            
    17.             RenderGameObject.GetComponent<Rigidbody>().position = GetComponentInParent<Rigidbody>().position+ secondCoordinatePosition;
    18.             RenderGameObject.GetComponent<Rigidbody>().rotation =  secondCoordinateRotation;
    19.             RenderGameObject.GetComponent<Transform>().localScale = secondCoordinateScale;
    20.  
    21.         }
    22.         else {
    23.             RenderGameObject.GetComponent<Rigidbody>().position = GetComponentInParent<Rigidbody>().position;
    24.             RenderGameObject.GetComponent<Rigidbody>().rotation = GetComponentInParent<Rigidbody>().rotation;
    25.             RenderGameObject.GetComponent<Transform>().localScale = GetComponentInParent<Transform>().localScale;
    26.         }
    27.  
    28.  
    29.  
     
  12. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    The script I posted still doesn't interpolate, so using the camera in LateUpdate doesn't work. But is possible to do interpolation in case you need it, don't worry.

    If you access the transform in Fixedupdate it will access the value used for the simulation and not the position of the visual object.

    Try using the camera in FixedUpdate but don't take the transform.position, use the previous position saved in script.

    Code (CSharp):
    1. //Storing this value in the case you want to use the update positions
    2.         lastUpdatePosition = transform.position;
    3.         lastUpdateRotation = transform.rotation;
     
  13. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Yes, breaking the object into two parts is a solution, in my case I started with this idea but I found it difficult to maintain as I had to add other objects, and I ended up changing.

    Definitely depends on the project and ubergeekgames should evaluate which is better in his case.
     
  14. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    The interpolation I do keeping a small buffer of at least 3 or 4 positions, 3 for linear and 4 for bezier, in my case I would already have to do this because it is a networked Project.
     
  15. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    jvggp, I had tried that initially but it is simply too user-unfriendly to keep functional with any moderately complex scene - not to mention the performance impact of doubling the number of objects. It's definitely not an option for us, unfortunately.

    davidfrk - Interesting stuff. I had not thought of interpolation, I am curious what advantages that offers in your use case besides networking prediction?

    For my current issue, I have not had luck with fiddling with the order in which the scripts are called, and am thinking that it may be caused due to the way in which I am setting up my objects. For the third person camera, I have two empties, one to hold the camera's autocam and collision avoidance scripts, a child to act as an offset pivot so that the camera's default location relative to its target can be modified, and then the camera itself. So, I can't simply query the lastUpdatePosition and so on from the camera's Transform that is looked up in the camera script, since it is a child - and by looking up the component in the chain of parents, I would be getting the non-offset position.

    I think that in this case, the issue is that the children's Transforms are not being updated at the same time as the parent's. I'm not entirely sure in what order Unity updates them and what, if any, caching is involved.

    The ideal scenario would be a direct drop-in solution so that other components, such as the Unity third person camera, do not require modification at all. I'd like this to be as user-friendly as possible, but I simply don't know enough about how Unity's internals work in order to make it so.
     
  16. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Using the script I posted you can assume:
    • If you access the transform in FixedUpdate you will get the values used in physics.
    • If you access in Update or LateUpdate you will get the VisualTransform.
    I believe the problem is that if you put the camera in FixedUpdate it takes the wrong values, and if you put it in the LateUpdate the camera script doesn't do a good job because it assumes you have interpolation. Then maybe you should interpolate?

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. //Set this script to run first in script execution order
    6. public class VisualPositionTransform : MonoBehaviour
    7. {
    8.     bool afterFixedUpdate = false;
    9.     Vector3 fixedUpdatePosition;
    10.     Quaternion fixedUpdateRotation;
    11.  
    12.     Vector3 updatePosition;
    13.     Quaternion updateRotation;
    14.  
    15.     Vector3 targetPosition;
    16.     Quaternion targetRotation;
    17.     Vector3 prevTargetPosition;
    18.     Quaternion prevTargetRotation;
    19.  
    20.     public Vector3 testPositionOffset;
    21.     public Vector3 testRotationOffset;
    22.     public Transform debugPhysicsTransform;
    23.  
    24.     void Start()
    25.     {
    26.         fixedUpdatePosition = transform.position;
    27.         fixedUpdateRotation = transform.rotation;
    28.         prevTargetPosition = transform.position;
    29.         prevTargetRotation = transform.rotation;
    30.         updatePosition = transform.position;
    31.         updateRotation = transform.rotation;
    32.         targetPosition = transform.position;
    33.         targetRotation = transform.rotation;
    34.     }
    35.  
    36.     void FixedUpdate()
    37.     {
    38.         transform.position = fixedUpdatePosition;
    39.         transform.rotation = fixedUpdateRotation;
    40.         afterFixedUpdate = true;
    41.     }
    42.  
    43.     void Update()
    44.     {
    45.         if (afterFixedUpdate)
    46.         {
    47.             fixedUpdatePosition = transform.position;
    48.             fixedUpdateRotation = transform.rotation;
    49.             afterFixedUpdate = false;
    50.  
    51.             prevTargetPosition = targetPosition;
    52.             prevTargetRotation = targetRotation;
    53.  
    54.             //Set targetPosition, whatever you want
    55.             targetPosition = testPositionOffset + fixedUpdatePosition;
    56.             targetRotation = Quaternion.Euler(testRotationOffset) * fixedUpdateRotation;
    57.         }
    58.  
    59.         VisualUpdate();
    60.     }
    61.  
    62.     void VisualUpdate()
    63.     {
    64.         //Interpolate
    65.         float t = (Time.time - Time.fixedTime) / Time.fixedDeltaTime;
    66.         transform.position = Vector3.Lerp(prevTargetPosition, targetPosition, t);
    67.         transform.rotation = Quaternion.Lerp(prevTargetRotation, targetRotation, t);
    68.  
    69.         //Storing this value in the case you want to use the update position
    70.         updatePosition = transform.position;
    71.         updateRotation = transform.rotation;
    72.  
    73.         if (debugPhysicsTransform != null)
    74.         {
    75.             debugPhysicsTransform.position = fixedUpdatePosition;
    76.             debugPhysicsTransform.rotation = fixedUpdateRotation;
    77.         }
    78.     }
    79. }
    80.  
     
  17. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Unity third person camera probably assumes that the character will stay vertically, in this case, will not work without modifications if you intend to rotate the character.
     
  18. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    This is very useful. However, I do not believe that interpolation is the cause of the present issue. I seem to be able to have a dramatic effect on what happens simply by changing the specific time in which the ThirdPersonUserControl is updated - the earlier link posted by jvggp is super helpful for better understanding that order, but I'm still not sure why I'm seeing this behavior:

    - FixedUpdate(): Uses the visual position of the camera, not the physics position. This is unexpected because I assumed that, according to that chart, FixedUpdate() would be called for every object in turn, sorted by script priority, of which the coordinate mapper executes first.
    - Update(): Uses the visual position of the camera, not the physics position. This is expected, since it should be running after the Update() method of the coordinate mapper swaps the positions to visual. However...
    - Update(), but modifying the coordinate mapper script to set the transform to the visual position in LateUpdate() instead of Update(): this has the same behavior as in the prior case, which is odd, because I would assume that LateUpdate() is called for every object, sorted by priority, *after* Update() has been called for every object. So, it should still be using the physics position, which is exactly the behavior that I want as that would maximize compatibility with other scripts.
    - LateUpdate(): This breaks the physics on the character - it freezes him in space, just like the very first issue I saw in the first post. I believe this is because the physics forces being added to the character would only be applied to the visual position, so they're immediately overwritten - so this case makes sense.

    For completeness, the third person user control script - near identical to the stock version. And just to double check, this still works perfectly if the coordinate mapper is disabled:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityStandardAssets.CrossPlatformInput;
    4.  
    5. namespace UnityStandardAssets.Characters.ThirdPerson
    6. {
    7.     [RequireComponent(typeof (ThirdPersonCharacter))]
    8.     public class ThirdPersonUserControl : MonoBehaviour
    9.     {
    10.         private ThirdPersonCharacter m_Character; // A reference to the ThirdPersonCharacter on the object
    11.         private Transform m_Cam;                  // A reference to the main camera in the scenes transform
    12.         private Vector3 m_CamForward;             // The current forward direction of the camera
    13.         private Vector3 m_Move;
    14.         private bool m_Jump;                      // the world-relative desired move direction, calculated from the camForward and user input.
    15.  
    16.        
    17.         private void Start()
    18.         {
    19.             // get the transform of the main camera
    20.             if (Camera.main != null)
    21.             {
    22.                 m_Cam = Camera.main.transform;
    23.             }
    24.             else
    25.             {
    26.                 Debug.LogWarning(
    27.                     "Warning: no main camera found. Third person character needs a Camera tagged \"MainCamera\", for camera-relative controls.", gameObject);
    28.                 // we use self-relative controls in this case, which probably isn't what the user wants, but hey, we warned them!
    29.             }
    30.  
    31.             // get the third person character ( this should never be null due to require component )
    32.             m_Character = GetComponent<ThirdPersonCharacter>();
    33.         }
    34.  
    35.  
    36.         private void FixedUpdate()
    37.         {
    38.             if (!m_Jump)
    39.             {
    40.                 m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
    41.             }
    42.             UpdateInput();
    43.         }
    44.  
    45.         private void OnGUI()
    46.         {
    47.             GUI.Label(new Rect(0, 0, 100, 100), m_CamForward.ToString());
    48.         }
    49.  
    50.         // Fixed update is called in sync with physics
    51.         private void UpdateInput()
    52.         {
    53.             Debug.Log("Camera - FixedUpdate");
    54.             // read inputs
    55.             float h = CrossPlatformInputManager.GetAxis("Horizontal");
    56.             float v = CrossPlatformInputManager.GetAxis("Vertical");
    57.             bool crouch = Input.GetKey(KeyCode.C);
    58.  
    59.             // calculate move direction to pass to character
    60.             if (m_Cam != null)
    61.             {
    62.                 // calculate camera relative direction to move:
    63.                 m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized;
    64.                 m_Move = v*m_CamForward + h*m_Cam.right;
    65.             }
    66.             else
    67.             {
    68.                 // we use world-relative directions in the case of no main camera
    69.                 m_Move = v*Vector3.forward + h*Vector3.right;
    70.             }
    71. #if !MOBILE_INPUT
    72.             // walk speed multiplier
    73.             if (Input.GetKey(KeyCode.LeftShift)) m_Move *= 0.5f;
    74. #endif
    75.  
    76.             // pass all parameters to the character control script
    77.             m_Character.Move(m_Move, crouch, m_Jump);
    78.             m_Jump = false;
    79.         }
    80.     }
    81. }
    82.  
     
  19. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    Yup, that's another reason why I'm (ideally! :D) keeping everything in Unity's coordinate space for physics, and just setting the rendering position and rotation.
     
  20. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    To clarify, I think the underlying issue here is I fundamentally do not understand how Unity's script execution model works. :D Specifically, given two scripts, are they updated like this every frame?

    Object1: FixedUpdate()
    Object2: FixedUpdate()
    Object1: Update()
    Object2: Update()
    Object1: LateUpdate()
    Object2: LateUpdate()

    Or like this?

    Object1: FixedUpdate()
    Object1: Update()
    Object1: LateUpdate()
    Object2: FixedUpdate()
    Object2: Update()
    Object2: LateUpdate()

    The former is what I assume is happening based on what seems logical given the execution order and based on some debug logging, but maybe it's possible that the debug log does not work the way I think it does and the second order is what's happening - which would explain the behavior that I'm currently seeing.
     
  21. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Correct is

    Object1: FixedUpdate()
    Object2: FixedUpdate()
    Object1: Update()
    Object2: Update()
    Object1: LateUpdate()
    Object2: LateUpdate()

    FixedUpdate is independent of update, so it's possible

    Object1: FixedUpdate()
    Object2: FixedUpdate()
    Object1: FixedUpdate()
    Object2: FixedUpdate()
    Object1: Update()
    Object2: Update()
    Object1: LateUpdate()
    Object2: LateUpdate()
    Object1: Update()
    Object2: Update()
    Object1: LateUpdate()
    Object2: LateUpdate()
     
  22. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Ow, Are you using the coordinate change script on the camera too?

    In this case you should have the camera on FixedUpdate. Collision avoidance scripts, everything on FixedUpdate. And make sure Rigidbody.interpolation is none.

    Could you make a video showing the problem?
     
    Last edited: Oct 3, 2018
  23. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    Yes. It's setup like so:

     
  24. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    A video or a scene with the problem would be good.
     
  25. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Could be the FixedUpdate of the camera running before the FixedUpdate of the other objects.

    Make a LateFixedUpdate for the camera, just as you did the PreUpdate before. Makes sense, you can't use LateUpdate.
     
    Last edited: Oct 3, 2018
  26. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Better, duplicate the VisualTransform script and use the second version on the camera with last execution order. Haha
     
  27. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    Hmm. I've played around with it in a repro project, and haven't been able to find a setup that works. Seems to work okay until rotation is added to the mix, and upon further inspection (read: lots of debug.log() :D) it seems that the problem may be related to the rigid body physics instead. I've dug into the documentation

    Here's the project - you may have to open the scene in the Samples folder. https://www.dropbox.com/s/skdb4sivhkjg30i/objecttransform_repro.zip?dl=0
     
  28. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Ok, It's because you're using the animator to move. Which occurs between the update and the lateupdate

    In this case the VisualUpdate should be in LateUpdate.
     
  29. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
  30. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    I duplicated the script to make sure the camera would run after the other objects
     
  31. IanNicolades

    IanNicolades

    Joined:
    Oct 1, 2016
    Posts:
    40
    You, sir, are a gentleman and a scholar. That has completely solved my issue - a combination of the execution order of the camera and the rest of the objects as you thought, and by comparing your functional example with mine, the discovery of a flipped coordinate in my conversion code. Once the camera issue was resolved, it was simple to discover that the character's movement on the X axis was flipped.

    Completely solved now - thank you very much for your help! :D
     
  32. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    I'm glad I was able to help!