Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Animation Transition Interpolation

Discussion in 'Animation' started by Partel-Lang, Oct 6, 2019.

  1. Partel-Lang


    Jan 2, 2013

    I was working on idle animations, namely switching between multiple idles to get infinite randomized idle behaviour, but noticed that the transitions between the idles were looking pretty bad with animation "snapping" from one state to another. That is the result of Mecanim's linear transition interpolation. So I made another test with a Direct blend tree and a script that blends the weights of the motion clips with sine interpolation.

    Here's a comparison:

    Notice the linear guy on the left snapping from one pose to another while the other guy looks pretty nice and smooth. That is because nothing in the way humans move is linear, we are physical beings so acceleration is always smooth. In that video it is just noticeable, but in VR the problem is painfully immersion breaking.

    So I was thinking, it would be great if Unity added a sine interpolation option to transition settings in Mecanim so everyone would be able to get smooth natural looking transitions without messy direct blend tree hacks.

    Basically just pass the transition weight parameter through this function and call it a day :)

    Code (CSharp):
    1. private static float InOutSine(float w)
    2. {
    3.       return -0.5f * (Mathf.Cos(Mathf.PI * w) - 1f);
    4. }

    Ony, LudiKha, AthrunVLokiz and 6 others like this.
  2. Kybernetik


    Jan 3, 2013
    I just released an update for Animancer which adds a Custom Fade system to do this sort of thing very easily (without needing to do anything special with Blend Trees, you just play animations normally).
    Last edited: Sep 18, 2022
    Ony and SenseEater like this.
  3. SenseEater


    Nov 28, 2014
    Updated to Animancer v5 as soon as it landed and must say the Custom Fade works brilliantly! Thanks a lot :)
  4. SenseEater


    Nov 28, 2014
    @Kybernetik i was further looking into Inertial Blending with Animancer based on the GDC talk linked. Any hints where can i get started with implementation of this sorts?

  5. Kybernetik


    Jan 3, 2013
    The Damping example is definitely the place to start. I could imagine inertialization being implemented as a similar Animation Job which constantly tracks the pose/velocity then intercepts a fade like the Custom Fade system to end it immediately and apply its own blending between the animation stream and pose/velocity captured at the start of the fade.

    I'd be very interested to see what you come up with. I wish I had more time to experiment with the Animation Job system because there are quite a few interesting ideas I want to try (like implementing a retargeting system for Generic rigs).
    SenseEater likes this.
  6. SenseEater


    Nov 28, 2014
    Thanks for the pointer. I was certain i need to delve into Animation Jobs for internalization. with V5 , it will be so much easier to get this rolling.

    I will be most certainly sharing my progress here and a new thread as well , as soon as i have this in presentable state.
  7. joshuacwilde


    Feb 4, 2018
    @Partel-Lang would you mind elaborating on how you made those smoothed animations in unity? It would be much appreciated!
  8. Partel-Lang


    Jan 2, 2013
    I wish there was a better way as this is very tedious and limited, but here it goes..

    1. Add all the anims you wish to use into a blend tree, set bled type to Direct.
    2. Add a unique parameter for every clip, like Idle 0, Idle 1, Idle 2, etc
    3. Make a DirectMotion class:

    Code (CSharp):
    1. public class DirectMotion
    2.     {
    3.         private float weight;
    4.         private int hash;
    6.         public DirectMotion(string name, float weight)
    7.         {
    8.             this.weight = weight;
    9.             hash = Animator.StringToHash(name);
    10.         }
    12.         // Uses Mecanim's Direct blend trees for cross-fading
    13.         public void DirectCrossFade(Animator animator, bool to, float crossfadeTime)
    14.         {
    15.             float target = to ? 1f : 0f;
    17.             if (weight != target)
    18.             {
    19.                 weight = Mathf.MoveTowards(weight, target, Time.deltaTime * (1f / crossfadeTime));
    20.             }
    21.             animator.SetFloat(hash, InOutSine(weight));
    22.         }
    24.         private static float InOutSine(float t)
    25.         {
    26.             return -0.5f * (Mathf.Cos(Mathf.PI * t) - 1f);
    27.         }
    28.     }
    4. Declare an array of DirectMotions:
    Code (CSharp):
    1. private DirectMotion[] idleMotions;
    5. Construct the motions in Start(). Names must match the Animator parameters:
    Code (CSharp):
    1. idleMotions = new DirectMotion[10]
    2.         {
    3.             new DirectMotion("Idle 0", 1f),
    4.             new DirectMotion("Idle 1", 0f),
    5.             new DirectMotion("Idle 2", 0f),
    6.             new DirectMotion("Idle 3", 0f),
    7.             new DirectMotion("Idle 4", 0f),
    8.             new DirectMotion("Idle 5", 0f),
    9.             new DirectMotion("Idle 6", 0f),
    10.             new DirectMotion("Idle 7", 0f),
    11.             new DirectMotion("Idle 8", 0f),
    12.             new DirectMotion("Idle 9", 0f),
    13.         };
    6. In Update(), update the DirectMotions like this:
    Code (CSharp):
    1. for (int i = 0; i < idleMotions.Length; i++)
    2.         {
    3.             idleMotions[i].DirectCrossFade(animator, i == currentIdleIndex, idleCrossfadeTime);
    4.         }
    So to cross-fade to another idle, all you need to do is change currentIdleIndex.

    But as you see, that is all super hacky and limited, can only use for switching between idles basically. Would be much better to have a smooth interpolation option in Animator transitions.
    Last edited: Aug 7, 2020
    Ony, dibdab and SenseEater like this.
  9. dibdab


    Jul 5, 2011
    it's very interesting. haven't seen using hash with setFloat.

    Crossfade cannot do direct fadetime modification, but
    might be combined with offset
    CrossFade(string stateName, float normalizedTransitionDuration, int layer, float normalizedTimeOffset, float normalizedTransitionTime);

    would need to start animation always one updatedelta later
    (again have no idea if this really works)

    also maybe if using animator.Play() for every update
    and setting Blend parameter every update
    can do that

    there's another thing
    mecanim can set 2 transitions to the same state
    and can have different fadecurves (but haven't tried what that does)
  10. AcidArrow


    May 20, 2010
    Since animator is surviving into the DOTS / ECS era, can we maybe get this feature? It should be an easy win.

    Unreal has it, and has had it for years. @ChinnyBJ @Mecanim-Dev

    LoicLeGrosFrere and Alex54620 like this.