Search Unity

Strange Animation Blending with AnimationLayerMixerPlayable

Discussion in 'Animation' started by magique, Jul 21, 2020.

  1. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    I'm working with Playables trying to create my own animation controller system, but running into some issues when I switched from AnimationMixerPlayable to AnimationLayerMixerPlayable. I have a basic kind of blend tree with idle, walk, run and it works fine when I use AnimationMixerPlayable, but when I switch to AnimationLayerMixerPlayable, the animations blend very strangely.

    See video below where the first part shows the correct blending with AnimationMixerPlayable and the second part shows incorrect blending with AnimationLayerMixerPlayable. Literally, the only thing different is the type of playable being used:



    Documentation for Playables is thin and tutorials on using it are hard to find so I'm kind of winging this trying to figure out how to make it all work. Does anyone know why the blending would work differently between these two playables?

    Also, note that at times the walking/running looks correct, but that's right at the point where the blending is 100% for walk or run and 0% for any other inputs.
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I have no idea why it would be doing that, but if you are trying to replicate a blend tree you should probably be using a regular mixer, not a layer mixer. The primary differences are that layer mixers support masks and additive blending, which are not relevant to that situation.
     
  3. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    No, I'm doing more than that. In addition to blending the idle, walk, and run, I also want to be able to inject an addiive animation such as waving while walking, attacking while running, etc. That's the whole reason I switched to the layers because it was the only way to make it happen.

    I'm really hoping someone has some clue as to what I could be doing wrong. You're the Animancer author so I could certainly use some advice. Maybe it's just something simple in my setup. I really am doing all this kind of blind because there are no docs on using the layer mixer or examples for most of the Playables system.
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Just use multiple mixers. Animancer would create a graph something like this:
    Code (CSharp):
    1. - Layer Mixer
    2.     - Regular Mixer for layer 0
    3.         - Regular Mixer for Idle/Walk/Run
    4.             - Animation Clip Playable for Idle
    5.             - Animation Clip Playable for Walk
    6.             - Animation Clip Playable for Run
    7.         - Playables for other clips and stuff
    8.     - Regular Mixer for layer 1
     
  5. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    Hmmm. I thought about that, but then I thought that you can only set a single source playable for the playable output so how does it play both mixers at the same time? I tried multiple outputs, but couldn't seem to get them to work together. This is one of those areas that just isn't clearly explained in the docs.
     
  6. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The layer mixer is the only one connected to the output. The other mixers are connected to the layer mixer. And the animation playables are connected to those mixers.

    It might be helpful to download Animancer Lite and the Playable Graph Visualiser so you can see what my graphs look like in the examples (Linear Blending in particular).
     
  7. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    I see. Thanks for the advice. I will try to take that approach in my next attempt.
     
  8. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    @Kybernetik I can't thank you enough. Your advice sent me in the right direction and I have it fully working now. I think the light bulb is starting to go on with how this is supposed to be structured. Thank you so much.
     
  9. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    @Kybernetik I thought I had this working perfectly, but the blending doesn't work as expected. I struggled with this for a long time today thinking I must be doing something wrong, but the same issue is happening when I simply use the sample script MixAnimationSample.cs provided by Unity. Watch the video and you'll see how the blending between walk and run gets all jerky.



    Here is the script provided as an example by Unity, which yields the same results as seen in the video:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Playables;
    3. using UnityEngine.Animations;
    4. [RequireComponent(typeof(Animator))]
    5. public class MixAnimationSample : MonoBehaviour
    6. {
    7.     public AnimationClip clip0;
    8.     public AnimationClip clip1;
    9.     public float weight;
    10.     PlayableGraph playableGraph;
    11.     AnimationMixerPlayable mixerPlayable;
    12.  
    13.     void Start()
    14.     {
    15.         // Creates the graph, the mixer and binds them to the Animator.
    16.         playableGraph = PlayableGraph.Create();
    17.  
    18.         var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", GetComponent<Animator>());
    19.  
    20.         mixerPlayable = AnimationMixerPlayable.Create(playableGraph, 2);
    21.         playableOutput.SetSourcePlayable(mixerPlayable);
    22.  
    23.         // Creates AnimationClipPlayable and connects them to the mixer.
    24.         var clipPlayable0 = AnimationClipPlayable.Create(playableGraph, clip0);
    25.         var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);
    26.  
    27.         playableGraph.Connect(clipPlayable0, 0, mixerPlayable, 0);
    28.         playableGraph.Connect(clipPlayable1, 0, mixerPlayable, 1);
    29.  
    30.         // Plays the Graph.
    31.         playableGraph.Play();
    32.     }
    33.  
    34.     void Update()
    35.     {
    36.         weight = Mathf.Clamp01(weight);
    37.         mixerPlayable.SetInputWeight(0, 1.0f - weight);
    38.         mixerPlayable.SetInputWeight(1, weight);
    39.     }
    40.  
    41.     void OnDisable()
    42.     {
    43.         // Destroys all Playables and Outputs created by the graph.
    44.         playableGraph.Destroy();
    45.     }
    46. }
    47.  
     
  10. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You need to implement Synchronisation to keep the walk and run animations at the same normalized time.
     
  11. magique

    magique

    Joined:
    May 2, 2014
    Posts:
    4,030
    Interesting that you said that because I was thinking that was the issue right off the bat and I tried to sync them, but didn't have any success, but I guess I gave up too quickly. I will look at it closer and try to get that working.
     
  12. forestrf

    forestrf

    Joined:
    Aug 28, 2010
    Posts:
    231
    Mixing with a LayerMixer isn't the same as a Mixer
    Mixer is the sum of all the clips each one multiplied by a weight.
    LayerMixer overrides previous data with a weight.

    Starting from last port to first port on a layer mixer and assuming the sum of all weights will be <= 1, the weights for each input port would be calculated this way:
    Code (CSharp):
    1. AnimationLayerMixerPlayable layerMixer; // Fill this somewhere
    2. List<float> weightWantedForPort; // Fill this somewhere
    3.  
    4. float remainingWeight = 1;
    5. for (int i = layer.Playable.GetInputCount() - 1; i >= 0; i--) {
    6.     float wantedWeight = weightWantedForPort[i];
    7.     layerMixer.SetInputWeight(i, wantedWeight / remainingWeight);
    8.     remainingWeight -= wantedWeight;
    9. }
    I have no idea how to handle weights larger than 1 or how would they work with this kind of mixer.

    This is interesting because it allows you to set up a clip (mixer, or whatever) on the first input, with a weight of 1, and use it as the default so in case a property isn't animated, it will fall back to the animated property of this clip instead of the unreliable default property stored on the animator.
    To do this, set that default clip to the input port 0 and on the previous code iterate up to
    i >= 1
    instead of
    i >= 0
    .
     
  13. Bogdan_Nikolayev

    Bogdan_Nikolayev

    Joined:
    Dec 29, 2020
    Posts:
    9
    I have a similar problem when mixing AnimatorControllerPlayable and AnimationClipPlayable with AnimationLayerMixerPlayable.

    Please, take a look at this thread. I would be grateful for any help.