Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Frame Skip when connecting playable to AnimationMixerPlayable

Discussion in 'Animation' started by Felipeota, Feb 9, 2018.

  1. Felipeota

    Felipeota

    Joined:
    May 30, 2013
    Posts:
    7
    I am using an AnimationMixerPlayable to chose between many different clips. When I first need a new clip I connect it to the mixer in a PlayableScript in the PrepareFrame method, since the manual says it's there where the topological changes to the graph should be applied (also if I try to connect or disconnect in a FixedUpdate of a Monobehaviour Unity crashes). When I first connect a clip and play it I can see an empty frame between the two animations, when the clip is played again (it is already connected) this doesn't happen. How can I dinamically change an AnimationMixerPlayable inputs without this skipping?

    Here's the source of the PlayableScript:

    Code (CSharp):
    1. public class BlenderPlayableBehaviour : PlayableBehaviour
    2. {
    3.     public AnimationMixerPlayable mixerPlayable;
    4.     public GameAnimator.GameAnimationState NextToPlay, Playing;
    5.     int emptySlot = 0;
    6.  
    7.     public override void PrepareFrame(Playable playable, FrameData info)
    8.     {
    9.         if (NextToPlay != null){
    10.             var graph = playable.GetGraph();
    11.             if (Playing != null){
    12.                 Playing.Playable.Pause();
    13.                 mixerPlayable.SetInputWeight(Playing.Slot, 0.0f);
    14.             }          
    15.             if (NextToPlay.Slot == -1){
    16.                 NextToPlay.Slot = emptySlot;
    17.                 emptySlot++;
    18.                 if (mixerPlayable.GetInputCount() < emptySlot){
    19.                     mixerPlayable.SetInputCount(emptySlot);
    20.                 }
    21.                 graph.Connect(NextToPlay.Playable, 0, mixerPlayable, NextToPlay.Slot);
    22.             }
    23.             NextToPlay.Playable.Play();
    24.             mixerPlayable.SetInputWeight(NextToPlay.Slot, 1.0f);
    25.             Playing = NextToPlay;
    26.             NextToPlay = null;
    27.         }
    28.         base.PrepareFrame(playable, info);
    29.     }
    30. }
    If I implement blending it is even more noticeable, but that's the minimum in which I get it to happen.
     
  2. mangax

    mangax

    Joined:
    Jul 17, 2013
    Posts:
    334
    i haven't tested your method, but try to make these changes after update frame ending.. maybe it's effect applied on same update frame when things gets rendered?
     
  3. Felipeota

    Felipeota

    Joined:
    May 30, 2013
    Posts:
    7
    Like in a coroutine with yield new WaitForEndOfFrame()? I'll try that but the thing is I tried to make the changes outside of the PlayableBehaviour and unity crashes hard.
     
  4. mangax

    mangax

    Joined:
    Jul 17, 2013
    Posts:
    334
    unity don't crash with me.. and it's better to avoid beta versions..

    what i usually do to avoid these issues..i connect all animations that i will use on mixer and set default input weight as 1f , then whenever i need to blend to another clip i simply change weights.. i don't connect or reconnect whenever i try to play a single clip..

    in your case, i would suggest you to try set the new weights on the next update frame.. so you give unity one frame to prepare these clips or something internally.. maybe your issue is setting inputs / connecting / weights in one frame is upsetting it's internals .. here are some edits..

    Code (CSharp):
    1. public class BlenderPlayableBehaviour : PlayableBehaviour
    2. {
    3.     public AnimationMixerPlayable mixerPlayable;
    4.     public GameAnimator.GameAnimationState NextToPlay, Playing;
    5.     int emptySlot = 0;
    6.     bool updateWeights=false;
    7.  
    8.     public override void PrepareFrame(Playable playable, FrameData info)
    9.     {
    10.         if (NextToPlay != null && updateWeights){
    11.             var graph = playable.GetGraph();
    12.             if (Playing != null){
    13.                 Playing.Playable.Pause();
    14.                 mixerPlayable.SetInputWeight(Playing.Slot, 0.0f);
    15.             }        
    16.  
    17.             NextToPlay.Playable.Play();
    18.             mixerPlayable.SetInputWeight(NextToPlay.Slot, 1.0f);
    19.             Playing = NextToPlay;
    20.             NextToPlay = null;
    21.              updateWeights=false;
    22.            }  
    23.             if (NextToPlay != null && NextToPlay.Slot == -1){
    24.                 NextToPlay.Slot = emptySlot;
    25.                 emptySlot++;
    26.                 if (mixerPlayable.GetInputCount() < emptySlot){
    27.                     mixerPlayable.SetInputCount(emptySlot);
    28.                 }
    29.                 graph.Connect(NextToPlay.Playable, 0, mixerPlayable, NextToPlay.Slot);
    30.  
    31.                  updateWeights=true;
    32.             }
    33.          
    34.        
    35.         base.PrepareFrame(playable, info);
    36.     }
    37. }
     
  5. Felipeota

    Felipeota

    Joined:
    May 30, 2013
    Posts:
    7
    I did the topology modification in a LateUpdate (instead of the PrepareFrame like Unity docs say) and the problem persisted. So I called graph.Evaluate(0.0f) and then the frameskip disappeared.

    I couldn't call the Evaluate inside the PrepareFrame because it generated an infinite recursion. It didn't crash but also I didn't disconnect any playable (I disconnected playables when it crashed).
     
  6. mangax

    mangax

    Joined:
    Jul 17, 2013
    Posts:
    334
  7. Felipeota

    Felipeota

    Joined:
    May 30, 2013
    Posts:
    7
    Nope, it works only with the evaluate with a 0 timestep. Apparently the animator only takes into account topology changes on evaluate, and if you do your changes on prepareframe then they will become available in the next call to evaluate