Search Unity

Recreate PlayableGraph every time a new animation needs to play?

Discussion in 'Animation' started by BMayzak, Jul 23, 2020.

  1. BMayzak

    BMayzak

    Joined:
    Nov 7, 2016
    Posts:
    22
    I just want to start off by saying that the documentation for PlayableGraphs are abysmal. I'm sure anyone in this part of the forums is already aware, but just venting how little information there is for such a complicated system.

    The examples provided by Unity are swell if all you want to do is blend from one animation to the other and that's it. Anything else, and it's the wild west of finding random youtube videos and obscure posts around the internet.

    *sigh*

    Anyways.. I have been banging my head against a wall trying to get my own custom playable solution going and have somewhat promising results, but I know I am not doing this optimally.

    I have a system set up where I have top animation and bottom animation clips for my character and depending on what the character is doing those (either top or bottom) will change immediately while unchanged animations will continue to play. I'm using a layer mask so that the character can run and swing a sword at the same time.

    I have this working with my current implementation, but I have to repeatedly Destroy and regenerate the PlayableGraph every time I want to change the animations which seems completely wrong.

    I have yet to find a single example of CHANGING animations in a PlayableGraph (a pretty common thing for a video game you would think..) but here we are..

    I tried using Animancer and even paid for the Pro version but unfortunately found it wasn't able to handle switching back and forth between animations quickly without the states somehow preserving the animation data and glitching out (also doesn't seem to be have the capability to manually update the deltaTime of the PlayableGraph which is required to do slow motion in my game). This is likely just specific to my implementation, otherwise it seems like a good plugin.

    So... if anyone has any ideas, I would be super grateful. I haven't noticed a drop in performance, but would like to implement this the correct way.

    Here's the code I run after setting an animation:

    Code (CSharp):
    1.  
    2. (after updating clipBase and clipAction)
    3. if(playableGraph.IsValid()) {
    4.             playableGraph.Destroy();
    5.         }
    6.  
    7.         if (!playableGraph.IsValid()) {
    8.             GeneratePlayableGraph();
    9.         }
    10. }
    11.  
    12. void GeneratePlayableGraph() {
    13.  
    14.         playableGraph = PlayableGraph.Create();
    15.         playableGraph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
    16.  
    17.         var playableOutput = AnimationPlayableOutput.Create(playableGraph, "LayerMixer", _animator);
    18.        
    19.         _clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clipBase);
    20.         _clipPlayable2 = AnimationClipPlayable.Create(playableGraph, clipAction);
    21.  
    22.         _mixerPlayable = AnimationLayerMixerPlayable.Create(playableGraph, 2);
    23.        
    24.         _mixerPlayable.ConnectInput(0, _clipPlayable1, 0, 1.0f);
    25.         _mixerPlayable.ConnectInput(1, _clipPlayable2, 0, mixLevel);
    26.        
    27.         if(mask != null) {
    28.             _mixerPlayable.SetLayerMaskFromAvatarMask(1, mask);
    29.         }
    30.  
    31.         PlayableExtensions.SetTime(_clipPlayable1, _baseAnimationTime);
    32.         PlayableExtensions.SetTime(_clipPlayable2, _actionAnimationTime);
    33.  
    34.         PlayableExtensions.SetSpeed(_clipPlayable1, 1f);
    35.         PlayableExtensions.SetSpeed(_clipPlayable2, 1f);
    36.         playableOutput.SetSourcePlayable(_mixerPlayable);
    37.         playableGraph.Play();
    38.  
    39.         GraphVisualizerClient.Show(playableGraph);
    40.     }
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    animancer.Playable.Pause()
    and
    animancer.Evaluate(deltaTime)
    should do what you want.

    But if you're doing it yourself, you can always create new playables and connect/disconnect the inputs on your mixers as necessary.
     
  3. BMayzak

    BMayzak

    Joined:
    Nov 7, 2016
    Posts:
    22
    Thanks Kybernetik!

    So for my custom implementation I can just recreate new AnimationClipPlayables, disconnect the old clip playables from the _mixerPlayable, connect the newly created AnimationClipPlayables and that should work?

    And I'm glad to know Animancer can also handle custom deltaTimes! The main issue I was having was when I quickly went back and forth between two attack animations (quickly back and forth like 10 times) there would be some sort of carryover where instead of instantly playing the new animation to completion it would end prematurely. The more I would spam the attacks, the quicker the new animation would end. I was using a very similar implementation to the Layered animation example provided but was difficult to tell what was happening. In any event, I might ask about it on your forums, but probably good for me to understand the underlying functionality anyways.

    Thanks for your help! I appreciate it
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yeah, and you'll need to either destroy the old playables or keep track of them to reuse them.

    It would be great if you could send me a simple reproduction project for that Animancer issue because I can't think of anything that would cause behaviour like that.
     
  5. BMayzak

    BMayzak

    Joined:
    Nov 7, 2016
    Posts:
    22
    Thanks! I can try again later and post a video and code snippet
     
  6. BMayzak

    BMayzak

    Joined:
    Nov 7, 2016
    Posts:
    22
    Hey Kybernetik!

    Just wanted to update to let you know that I got my animations working with Animancer! (and now have fading, wooo!)

    I think the issue was that in my old set up I had split the top and bottom animations (with a mask for the top) and was sometimes setting the same animation to both the top and bottom, but the animation can only be playing on one layer at a time in Animancer. I tweaked my set up so there's always a base animation and then an action layer than can play over top and that fixed the wonkiness I was seeing.

    Thanks for the tip about the manual .Evaluate as well!! Animancer is already doing more than my current implementation, so I'm excited to see what else it can do (and with a dev that is around and answering questions, wooo :) )
     
  7. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You can't play a single state on multiple layers at the same time, but if you need to you can create a second state for the same animation using new ClipState, animancer.States.Create, or animancer.Layers[x].CreateState. That's not usually necessary though. I've always been able to do what I need by playing that animation on the base layer or swapping it between layers like in the Layers example.