Search Unity

AnimationMixerPlayable - Blendshape Blending Problems

Discussion in 'Animation' started by Arnai, Dec 18, 2018.

  1. Arnai

    Arnai

    Joined:
    Mar 23, 2017
    Posts:
    8
    Hello Playable-Animators,

    I've run into a peculiar problem with the AnimationMixerPlayable. Following scenario:

    I have a AnimationMixerPlayable as my output to my PlayableGraph. This mixer has two inputs, the facial animations "happy" and "angry" which both contain blendshapes. When I set the input weight for "happy" to 1, the animation shows as expected. But when I then set the input weights of "happy" to 0 and "angry" to 1, the blendshape weights for the "smile" shapes of the "happy" animation are retained. They are not changing anymore, but if they were for example at 0.3 when the switch from happy to angry occured, they'll stay at 0.3. As far as I understand blending, this cannot be intentional behaviour, far less so because everything works as expected when using Blend Trees in Mecanim instead.
    Interestingly, this error only occurs if the Animator component has a AnimatorController attached, so that seems to factor in somehow.

    Here's the code I used for testing:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Animations;
    5. using UnityEngine.Playables;
    6.  
    7. public class PlayableBugTest : MonoBehaviour {
    8.  
    9.     public bool GoToHappy;
    10.     public bool GoToAngry;
    11.  
    12.     public AnimationClip happy;
    13.     public AnimationClip angry;
    14.  
    15.     private Animator animator;
    16.     private RuntimeAnimatorController runtimeAnimController;
    17.  
    18.     private PlayableGraph playableGraph;
    19.     AnimationClipPlayable pHappy;
    20.     AnimationClipPlayable pAngry;
    21.     AnimationMixerPlayable mixerEmotionPlayable;
    22.  
    23.     // Use this for initialization
    24.     void Start () {
    25.         animator = GetComponent<Animator>();
    26.  
    27.         playableGraph = PlayableGraph.Create("ClairePlayableGraph");
    28.         playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
    29.         var playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", animator);
    30.  
    31.         // Create an Emotion Mixer
    32.         mixerEmotionPlayable = AnimationMixerPlayable.Create(playableGraph, 4);
    33.         playableOutput.SetSourcePlayable(mixerEmotionPlayable);
    34.  
    35.         // Wrap the clips in a playable
    36.         pHappy = AnimationClipPlayable.Create(playableGraph, happy);
    37.         pAngry = AnimationClipPlayable.Create(playableGraph, angry);
    38.  
    39.         // Connect to Emotion Mixer
    40.         playableGraph.Connect(pHappy, 0, mixerEmotionPlayable, 0);
    41.         playableGraph.Connect(pAngry, 0, mixerEmotionPlayable, 1);
    42.  
    43.  
    44.         // Plays the Graph
    45.         playableGraph.Play();
    46.     }
    47.    
    48.     // Update is called once per frame
    49.     void Update () {
    50.         if(GoToHappy)
    51.         {
    52.             mixerEmotionPlayable.SetInputWeight(0, 1.0f);
    53.             mixerEmotionPlayable.SetInputWeight(1, 0.0f);
    54.         }
    55.         if (GoToAngry)
    56.         {
    57.             mixerEmotionPlayable.SetInputWeight(0, 0.0f);
    58.             mixerEmotionPlayable.SetInputWeight(1, 1.0f);
    59.         }
    60.     }
    61.  
    62.  
    63.     void OnDisable()
    64.     {
    65.  
    66.         // Destroys all Playables and PlayableOutputs created by the graph.
    67.  
    68.         playableGraph.Destroy();
    69.  
    70.     }
    71. }
    72.  
     
  2. AbelChiu

    AbelChiu

    Joined:
    Nov 29, 2016
    Posts:
    12
    Hi,
    Before call SetInputWeight(0,1),you can reset the BlendShape's Value;
    Just like:
    var skinnedMeshList = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();

    var blendShapeCount = 0;
    Mesh shaderMesh;
    for (int i = 0; i < skinnedMeshList.Length; i++)
    {
    shaderMesh = skinnedMeshList.sharedMesh;
    if (shaderMesh == null)
    {
    continue;
    }
    blendShapeCount = shaderMesh.blendShapeCount;
    if (blendShapeCount == 0)
    {
    continue;
    }
    for(int j = 0; j < blendShapeCount; j++){
    skinnedMeshList.SetBlendShapeWeight(j, 0);
    }
    }

    hope this helps!
     
    Arnai likes this.
  3. Arnai

    Arnai

    Joined:
    Mar 23, 2017
    Posts:
    8
    Good call, I have tried this but it did not alleviate my problem. Does it work for you?
     
  4. AbelChiu

    AbelChiu

    Joined:
    Nov 29, 2016
    Posts:
    12
    If your runtimeAnimController is still working, you need to call animator.runtimeAnimtorController = null first,
    At the end call animator.runtimeAnimtorController = runtimeAnimController
     
    Arnai likes this.
  5. Arnai

    Arnai

    Joined:
    Mar 23, 2017
    Posts:
    8
    Wow, that actually seems to be the fix! Thanks a lot! How did you figure that out?
    EDIT: Nulling and reassigned the RuntimeAnimatorController seems to be all that is necessary to avoid "stuck" blendshapes. Explicitly setting the shapes to 0 appears to be unnecessary in this case.
    Of course, if any other script is trying to access the AnimatorController at runtime, this "fix" would cause a lot of problems. Hopefully there will be a real fix in the future.
     
    Last edited: Jan 16, 2019
  6. AbelChiu

    AbelChiu

    Joined:
    Nov 29, 2016
    Posts:
    12
    Agree with you about "Explicitly setting the shapes to 0 appears to be unnecessary in this case"!