Search Unity

Animator controller override issue with LateUpdate()

Discussion in 'Animation' started by Fullspike, Mar 15, 2019.

  1. Fullspike

    Fullspike

    Joined:
    Aug 12, 2017
    Posts:
    11
    Hello,
    I have a third person character with several set of animation that I change with dedicated Animator controller override whenever the character change the weapon:

    Code (CSharp):
    1. anim.runtimeAnimatorController = weapon.animOverrideTP;
    The animator correctly swap the controller and the animations clip.
    Nevertheless, when this is changed more than 1 time (first on awake, then during the game), I'm not able to rotate bones in LateUpdate() anymore, the following code works fine at start but after that the controller swap the jointUpper gets stuck in its rotation:

    Code (CSharp):
    1.  private void LateUpdate()
    2. {
    3. Quaternion target = Quaternion.Euler(animatorObservable.upperBodyRotation, jointUpper.transform.eulerAngles.y, jointUpper.transform.eulerAngles.z);
    4. jointUpper.transform.rotation = Quaternion.Slerp(jointUpper.transform.rotation, target,  ConstantsPlayer.upperBodyRotationSpeed * Time.deltaTime);
    5. }

    Have someone ever had this problem or know which could be the cause?
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Have you tried debugging your method to see if it's still setting the expected values? That will determine whether the problem is in your scripts or somewhere else.

    Override controllers aren't my favourite solution, but this should be exactly what they're made for so I wouldn't expect a bug like that.
     
  3. Fullspike

    Fullspike

    Joined:
    Aug 12, 2017
    Posts:
    11
    I made further tests:
    1. if I set the rotation of jointUpper directly equals to target it is assigned correctly.
    2. Referring to the debug line of the following code
    - before changing the controller it prints 3 angle values which are going to be aligned (rotationUpdate and jointUpper rotation are always the same, they chase the target angle with the speed set in the Slerp)
    - after changing rotationUpdate and jointUpper (which are equals) change only a bit and never reach the target value.
    Code (CSharp):
    1.  Quaternion rotationUpdate = Quaternion.Slerp(jointUpper.transform.rotation, target, ConstantsPlayer.upperBodyRotationSpeed * Time.deltaTime);
    2. jointUpper.transform.rotation = rotationUpdate;
    3. Debug.Log(target.eulerAngles + " " + rotationUpdate.eulerAngles + " " + jointUpper.transform.rotation.eulerAngles + " " + s);
    It seems like something overrides the jointUpper rotation after the LateUpdate therefore the Slerp start parameter is not the value that I set in the previous frame. A tricky solution that I found is to store the jointUpper rotation in another variable updated in LateUpdate and to use it in the Slerp;
    Code (CSharp):
    1. Quaternion jointUpperRot;
    2.    
    3. void Awake () {
    4.         jointUpperRot = jointUpper.transform.rotation;
    5. }
    6.  
    7. private void LateUpdate()
    8. {
    9.         Quaternion target = Quaternion.Euler(animatorObservable.upperBodyRotation, jointUpper.transform.eulerAngles.y, jointUpper.transform.eulerAngles.z);
    10.         Quaternion rotationUpdate = Quaternion.Slerp(jointUpperRot , target, ConstantsPlayer.upperBodyRotationSpeed * Time.deltaTime);
    11.         jointUpper.transform.rotation = rotationUpdate;
    12.         jointUpperRot = rotationUpdate;
    13.     }
    I don't know if it's a bug but it is very strange.
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    If jointUpper is a bone controlled by an animation, then its rotation will get set to a value dictated by that animation every frame right before LateUpdate is called. So the animation sets the value it wants, then you Slerp a little bit away from that, then the animation sets the value, then you again Slerp a little bit away from that rather than continuing from the previous result of your Slerp, which is obviously not what you want.

    In this case, storing the previous value you set and Slerping from that to the target could be the correct approach for what you want. That will constantly move towards the target and slow down as it gets close.