Search Unity

Animation Transition Interpolation

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

  1. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,552
    Hey,

    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. }

    Cheers,
    Pärtel
     
    Ony, LudiKha, AthrunVLokiz and 6 others like this.
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    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

    SenseEater

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

    SenseEater

    Joined:
    Nov 28, 2014
    Posts:
    84
    @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

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    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

    SenseEater

    Joined:
    Nov 28, 2014
    Posts:
    84
    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

    joshuacwilde

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

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,552
    Hey,
    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;
    5.  
    6.         public DirectMotion(string name, float weight)
    7.         {
    8.             this.weight = weight;
    9.             hash = Animator.StringToHash(name);
    10.         }
    11.  
    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;
    16.  
    17.             if (weight != target)
    18.             {
    19.                 weight = Mathf.MoveTowards(weight, target, Time.deltaTime * (1f / crossfadeTime));
    20.             }
    21.             animator.SetFloat(hash, InOutSine(weight));
    22.         }
    23.  
    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

    dibdab

    Joined:
    Jul 5, 2011
    Posts:
    976
    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)
    2transit.jpg
     
  10. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,724
    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.