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:
(after updating clipBase and clipAction)
if(playableGraph.IsValid()) {
playableGraph.Destroy();
}
if (!playableGraph.IsValid()) {
GeneratePlayableGraph();
}
}
void GeneratePlayableGraph() {
playableGraph = PlayableGraph.Create();
playableGraph.SetTimeUpdateMode(DirectorUpdateMode.Manual);
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "LayerMixer", _animator);
_clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clipBase);
_clipPlayable2 = AnimationClipPlayable.Create(playableGraph, clipAction);
_mixerPlayable = AnimationLayerMixerPlayable.Create(playableGraph, 2);
_mixerPlayable.ConnectInput(0, _clipPlayable1, 0, 1.0f);
_mixerPlayable.ConnectInput(1, _clipPlayable2, 0, mixLevel);
if(mask != null) {
_mixerPlayable.SetLayerMaskFromAvatarMask(1, mask);
}
PlayableExtensions.SetTime(_clipPlayable1, _baseAnimationTime);
PlayableExtensions.SetTime(_clipPlayable2, _actionAnimationTime);
PlayableExtensions.SetSpeed(_clipPlayable1, 1f);
PlayableExtensions.SetSpeed(_clipPlayable2, 1f);
playableOutput.SetSourcePlayable(_mixerPlayable);
playableGraph.Play();
GraphVisualizerClient.Show(playableGraph);
}