内容将会持续更新,有错误的地方欢迎指正,谢谢!
拥有更好的学习体验 —— 不断努力,不断进步,不断探索 |
助力快速掌握 PlayableGraph 动画播放 为初学者节省宝贵的学习时间,避免困惑! |
前言:
通过Playable API来控制动画的播放,这种方式比使用传统的Animation组件或Animator组件更加灵活,可以实现更复杂的动画控制逻辑。这篇博客将想你展示用Playable API来实现动画的正播和倒播。
文章目录
- 一、创建Playable图
- 二、创建Playable输出节点
- 三、创建Playable动画剪辑
- 四、设置输出节点的输入源为动画剪辑
- 五、动画正播
- 六、动画倒播
一、创建Playable图
/// <summary>
/// 创建PlayableGraph和设置
/// </summary>
//创建一个PlayableGraph
PlayableGraph graph = PlayableGraph.Create();
//设置PlayableGraph的时间更新方式
graph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
创建PlayableGraph相当于在Unity中创建Timeline。
二、创建Playable输出节点
/// <summary>
/// 向PlayableGraph中添加AnimationTrack
/// </summary>
AnimationPlayableOutput.Create(PlayableGraph graph, string outName, Animator animator);
创建Playable输出节点相当于在Timeline中添加一个Animation Track。
三、创建Playable动画剪辑
/// <summary>
/// 向PlayableGraph中添加动画片段
/// </summary>
AnimationClipPlayable.Create(PlayableGraph graph, AnimationClip clip);
创建Playable动画剪辑相当于在Timelime中添加一个动画片段。
四、设置输出节点的输入源为动画剪辑
/// <summary>
/// 将动画剪辑绑定到输出节点上
/// </summary>
AnimationPlayableOutput.SetSourcePlayable(AnimationClipPlayable clipPlayable);
设置输出节点的输入源为动画剪辑相当于将动画片段添加到Animator中。
五、动画正播
//设置为正向播放
AnimationClipPlayable.SetSpeed(1);
//设置为从开始时间进行播放
AnimationClipPlayable.SetTime(0);
//连线(设置输出节点的输入源为动画剪辑)
AnimationPlayableOutput.SetSourcePlayable(AnimationClipPlayable clipPlayable);
//播放graph
PlayableGraph.Play();
通过设置动画片段的播放速度为1,表示为向前播放,同时设置播放的开始时间为动画片段的开始时间,开始播放graph时,动画正向播放。
六、动画倒播
//设置反正向播放
AnimationClipPlayable.SetSpeed(-1);
//设置为从结束时间进行播放
AnimationClipPlayable.SetTime(AnimationClipPlayable.GetAnimationClip().length);
//连线(设置输出节点的输入源为动画剪辑)
output.SetSourcePlayable(clipPlayable);
//播放graph
graph.Play();
通过设置动画片段的播放速度为-1,表示为向后播放,同时设置播放的开始时间为动画片段的结束时间,开始播放graph时,动画反向播放。
下面是动画的正反播放的完整代码:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
/// <summary>
/// 动画播放接口
/// </summary>
public interface IAnimationPlay
{
void PlayForward(Action OnPlayCall=null, Action OnCompleted = null);
void PlayBackward(Action OnPlayCall = null, Action OnCompleted = null);
void Stop(Action OnStopCall = null);
bool IsPlaying { get; }
}
/// <summary>
/// 异步等待器
/// </summary>
public class AsyncWaitUntil
{
private TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
bool completed = false;
public AsyncWaitUntil(Func<bool> predicate)
{
WaitUntil(predicate);
}
public Task Task => tcs.Task;
private async void WaitUntil(Func<bool> predicate)
{
while (!completed)
{
completed = predicate.Invoke();
await Task.Delay(200);
}
tcs.SetResult(null);
}
}
public class PlayableGraphAnimator : MonoBehaviour, IAnimationPlay, IAnimationClipSource
{
public Animator animator;
public AnimationClip clip;
protected PlayableGraph graph;
protected AnimationPlayableOutput output;
public bool IsPlaying { get { return graph.IsPlaying(); } }
private void Awake()
{
CrateGraph();
}
/// <summary>
/// 初始化一个Graph
/// </summary>
private void CrateGraph()
{
graph = CreatePlayableGraph();
output = CreatePlayableOutput(graph, "Animation", animator);
}
/// <summary>
/// 创建PlayableGraph和设置
/// </summary>
private PlayableGraph CreatePlayableGraph()
{
//创建一个PlayableGraph
PlayableGraph graph = PlayableGraph.Create();
//设置PlayableGraph的时间更新方式
graph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
return graph;
}
/// <summary>
/// 向PlayableGraph中添加AnimationTrack
/// </summary>
/// <param name="graph">PlayableGraph</param>
/// <param name="outName">AnimationTrack的名称</param>
/// <param name="animator">要添加的Track的类型</param>
/// <returns></returns>
private AnimationPlayableOutput CreatePlayableOutput(PlayableGraph graph, string outName, Animator animator)
{
return AnimationPlayableOutput.Create(graph, outName, animator);
}
/// <summary>
/// 向PlayableGraph中添加动画片段
/// </summary>
/// <param name="graph">PlayableGraph</param>
/// <param name="clip">动画片段</param>
/// <returns></returns>
private AnimationClipPlayable CreateClipPlayable(PlayableGraph graph, AnimationClip clip)
{
return AnimationClipPlayable.Create(graph, clip);
}
/// <summary>
/// 向前播放动画
/// </summary>
/// <param name="OnPlayCall">开始播放回调</param>
/// <param name="OnCompleted">播放完成回调</param>
public async void PlayForward(Action OnPlayCall = null, Action OnCompleted = null)
{
if (IsPlaying) return;
var clipPlayable = CreateClipPlayable(graph, clip);
//正向播放
clipPlayable.SetSpeed(1);
clipPlayable.SetTime(0);
output.SetSourcePlayable(clipPlayable);
graph.Play();
OnPlayCall?.Invoke();
await new AsyncWaitUntil(() => clipPlayable.GetTime() >= clip.length).Task;
OnCompleted?.Invoke();
graph.Stop();
}
/// <summary>
/// 向后播放动画
/// </summary>
/// <param name="OnPlayCall">开始播放回调</param>
/// <param name="OnCompleted">播放完成回调</param>
public async void PlayBackward(Action OnPlayCall = null, Action OnCompleted = null)
{
if (IsPlaying) return;
var clipPlayable = CreateClipPlayable(graph, clip);
//反向播放
clipPlayable.SetSpeed(-1);
clipPlayable.SetTime(clipPlayable.GetAnimationClip().length);
output.SetSourcePlayable(clipPlayable);
graph.Play();
OnPlayCall?.Invoke();
await new AsyncWaitUntil(() => clipPlayable.GetTime() <= 0).Task;
OnCompleted?.Invoke();
graph.Stop();
}
/// <summary>
/// 停止动画
/// </summary>
public void Stop(Action OnStopCall = null)
{
if (!IsPlaying) return;
graph.Stop();
OnStopCall?.Invoke();
}
public void GetAnimationClips(List<AnimationClip> results)
{
if (clip != null)
results.Add(clip);
}
private void OnDestroy()
{
graph.Destroy();
}
}
每一次跌倒都是一次成长 每一次努力都是一次进步 |
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!