Animation Transition Interpolation

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 :slight_smile:

private static float InOutSine(float w)
{
      return -0.5f * (Mathf.Cos(Mathf.PI * w) - 1f);
}

Cheers,
Pärtel

9 Likes

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

2 Likes

Updated to Animancer v5 as soon as it landed and must say the Custom Fade works brilliantly! Thanks a lot :slight_smile:

@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?

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

1 Like

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.

@Partel-Lang would you mind elaborating on how you made those smoothed animations in unity? It would be much appreciated!

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:
public class DirectMotion
    {
        private float weight;
        private int hash;

        public DirectMotion(string name, float weight)
        {
            this.weight = weight;
            hash = Animator.StringToHash(name);
        }

        // Uses Mecanim's Direct blend trees for cross-fading
        public void DirectCrossFade(Animator animator, bool to, float crossfadeTime)
        {
            float target = to ? 1f : 0f;

            if (weight != target)
            {
                weight = Mathf.MoveTowards(weight, target, Time.deltaTime * (1f / crossfadeTime));
            }
            animator.SetFloat(hash, InOutSine(weight));
        }

        private static float InOutSine(float t)
        {
            return -0.5f * (Mathf.Cos(Mathf.PI * t) - 1f);
        }
    }
  1. Declare an array of DirectMotions:
private DirectMotion[] idleMotions;
  1. Construct the motions in Start(). Names must match the Animator parameters:
idleMotions = new DirectMotion[10]
        {
            new DirectMotion("Idle 0", 1f),
            new DirectMotion("Idle 1", 0f),
            new DirectMotion("Idle 2", 0f),
            new DirectMotion("Idle 3", 0f),
            new DirectMotion("Idle 4", 0f),
            new DirectMotion("Idle 5", 0f),
            new DirectMotion("Idle 6", 0f),
            new DirectMotion("Idle 7", 0f),
            new DirectMotion("Idle 8", 0f),
            new DirectMotion("Idle 9", 0f),
        };
  1. In Update(), update the DirectMotions like this:
for (int i = 0; i < idleMotions.Length; i++)
        {
            idleMotions[i].DirectCrossFade(animator, i == currentIdleIndex, idleCrossfadeTime);
        }

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.

3 Likes

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)
6180756--676944--2transit.jpg

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. @UnityChinny @Mecanim-Dev

3 Likes