Last active
May 12, 2025 04:31
-
-
Save adammyhre/f0679dfc8809f2aa0d7a39e8ff6116ed to your computer and use it in GitHub Desktop.
Simple Wrapper for Unity Playables API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Animations; | |
using UnityEngine.Playables; | |
using MEC; // Uses More Effective Coroutines from the Unity Asset Store | |
public class AnimationSystem { | |
PlayableGraph playableGraph; | |
readonly AnimationMixerPlayable topLevelMixer; | |
readonly AnimationMixerPlayable locomotionMixer; | |
AnimationClipPlayable oneShotPlayable; | |
CoroutineHandle blendInHandle; | |
CoroutineHandle blendOutHandle; | |
public AnimationSystem(Animator animator, AnimationClip idleClip, AnimationClip walkClip) { | |
playableGraph = PlayableGraph.Create("AnimationSystem"); | |
AnimationPlayableOutput playableOutput = AnimationPlayableOutput.Create(playableGraph, "Animation", animator); | |
topLevelMixer = AnimationMixerPlayable.Create(playableGraph, 2); | |
playableOutput.SetSourcePlayable(topLevelMixer); | |
locomotionMixer = AnimationMixerPlayable.Create(playableGraph, 2); | |
topLevelMixer.ConnectInput(0, locomotionMixer, 0); | |
playableGraph.GetRootPlayable(0).SetInputWeight(0, 1f); | |
AnimationClipPlayable idlePlayable = AnimationClipPlayable.Create(playableGraph, idleClip); | |
AnimationClipPlayable walkPlayable = AnimationClipPlayable.Create(playableGraph, walkClip); | |
idlePlayable.GetAnimationClip().wrapMode = WrapMode.Loop; | |
walkPlayable.GetAnimationClip().wrapMode = WrapMode.Loop; | |
locomotionMixer.ConnectInput(0, idlePlayable, 0); | |
locomotionMixer.ConnectInput(1, walkPlayable, 0); | |
playableGraph.Play(); | |
} | |
public void UpdateLocomotion(Vector3 velocity, float maxSpeed) { | |
float weight = Mathf.InverseLerp(0f, maxSpeed, velocity.magnitude); | |
locomotionMixer.SetInputWeight(0, 1f - weight); | |
locomotionMixer.SetInputWeight(1, weight); | |
} | |
public void PlayOneShot(AnimationClip oneShotClip) { | |
if (oneShotPlayable.IsValid() && oneShotPlayable.GetAnimationClip() == oneShotClip) return; | |
InterruptOneShot(); | |
oneShotPlayable = AnimationClipPlayable.Create(playableGraph, oneShotClip); | |
topLevelMixer.ConnectInput(1, oneShotPlayable, 0); | |
topLevelMixer.SetInputWeight(1, 1f); | |
// Calculate blendDuration as 10% of clip length, | |
// but ensure that it's not less than 0.1f or more than half the clip length | |
float blendDuration = Mathf.Clamp(oneShotClip.length * 0.1f, 0.1f, oneShotClip.length * 0.5f); | |
BlendIn(blendDuration); | |
BlendOut(blendDuration, oneShotClip.length - blendDuration); | |
} | |
void BlendIn(float duration) { | |
blendInHandle = Timing.RunCoroutine(Blend(duration, blendTime => { | |
float weight = Mathf.Lerp(1f, 0f, blendTime); | |
topLevelMixer.SetInputWeight(0, weight); | |
topLevelMixer.SetInputWeight(1, 1f - weight); | |
})); | |
} | |
void BlendOut(float duration, float delay) { | |
blendOutHandle = Timing.RunCoroutine(Blend(duration, blendTime => { | |
float weight = Mathf.Lerp(0f, 1f, blendTime); | |
topLevelMixer.SetInputWeight(0, weight); | |
topLevelMixer.SetInputWeight(1, 1f - weight); | |
}, delay, DisconnectOneShot)); | |
} | |
IEnumerator<float> Blend(float duration, Action<float> blendCallback, float delay = 0f, Action finishedCallback = null) { | |
if (delay > 0f) { | |
yield return Timing.WaitForSeconds(delay); | |
} | |
float blendTime = 0f; | |
while (blendTime < 1f) { | |
blendTime += Time.deltaTime / duration; | |
blendCallback(blendTime); | |
yield return blendTime; | |
} | |
blendCallback(1f); | |
finishedCallback?.Invoke(); | |
} | |
void InterruptOneShot() { | |
Timing.KillCoroutines(blendInHandle); | |
Timing.KillCoroutines(blendOutHandle); | |
topLevelMixer.SetInputWeight(0, 1f); | |
topLevelMixer.SetInputWeight(1, 0f); | |
if (oneShotPlayable.IsValid()) { | |
DisconnectOneShot(); | |
} | |
} | |
void DisconnectOneShot() { | |
topLevelMixer.DisconnectInput(1); | |
playableGraph.DestroyPlayable(oneShotPlayable); | |
} | |
public void Destroy() { | |
if (playableGraph.IsValid()) { | |
playableGraph.Destroy(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment