KybernetikGames / animancer

Documentation for the Animancer Unity Plugin.
64 stars 8 forks source link

Fading out override layers (Game Advice) #135

Closed h-sigma closed 3 years ago

h-sigma commented 3 years ago

In my current project, the target layer is a property set on the clip/transition (currently only using Base = 0, Additive = 1, Override = 2). The target is a bike/character pair, and the Base layer contains all of the steering/drifting/idle/etc animations that are controlled "internally" by the bike controller. The Override layer is used by abilities and other stuff that like to define their own animations to play on caster/victim. The "internal" animations are defined in a scriptable object, but the override animations are serialized on the prefabs for easy setup of callbacks. My problem is that when the animation in the override layer ends, the switch to the base layer animation is quite abrupt (since that base layer never stops playing, there is no fade-in). What would be a good approach to fade out the Override layer? The animations being played can be looping, part of a sequence of animations (intro, loop, outro), any length, etc.

So I don't something like "always add an event at (length-0.5s) time that calls layer.fade(0.5s, 0) when you play a clip on this item" wouldn't work.

I could move all animations to the base layer, but my animations use End Events to change controller's state, so it might be hard to time when to fade in the normal animation.

KybernetikGames commented 3 years ago

Events that fade out the layer are likely how I'd try to do it. I can't think of anything else that might be better.

h-sigma commented 3 years ago
            {
                if (newState.IsLooping)
                {
                    Debug.LogWarning($"Trying to fade out a looping animation. Hint: It doesn't work, dummy. Playback Parameters:{playback}");
                }
                else
                {
                    float fadeoutTime = playback.fadeOutNormalizedTime.Value;
                    float normalizedTime = (newState.Length - fadeoutTime) / newState.Length;
                    newState.Events.Add(normalizedTime,
                        () => layer.StartFade(0.01f, fadeoutTime));
                }
            }

This is my current attempt. But it's stopping my end event from firing for some reason. I even set the target weight to 0.01f to prevent Stop() from being called as a consequence of the fading and not the clip ending.

KybernetikGames commented 3 years ago

StartFade is one of the methods that clears all events (like when playing a new animation). To prevent that you can just set AnimancerState.AutomaticallyClearEvents to false, start the fade, then set it back to true.

You could also try the new Exit Event System. Fade out the layer fully, then add an Exit Event which will be called when the weight reaches 0 instead of using a regular End Event based on its time.

h-sigma commented 3 years ago

Hmm. It seems it's not in my version. I'll have to ask the project owner to push to git with the latest version. Thankfully, the source is editable, so I added this myself for testing. It works event wise, but now I've run into a fundamental problem with my approach: since the upper layer is an override layer, when the weight fades, the model goes back to its T-Pose, instead of the underlying layer.

h-sigma commented 3 years ago

image How my animancer component looks like, for reference.

h-sigma commented 3 years ago

Actually, that was my bad. I was fading out the state instead of the layer. Works great now :)