Unity之PlayableGraph实现动画的正播和倒播

内容将会持续更新,有错误的地方欢迎指正,谢谢!
 

Unity之PlayableGraph实现动画的正播和倒播
     
TechX 坚持将创新的科技带给世界!

拥有更好的学习体验 —— 不断努力,不断进步,不断探索
TechX —— 心探索、心进取!

助力快速掌握 PlayableGraph 动画播放

为初学者节省宝贵的学习时间,避免困惑!


前言:

  通过Playable API来控制动画的播放,这种方式比使用传统的Animation组件或Animator组件更加灵活,可以实现更复杂的动画控制逻辑。这篇博客将想你展示用Playable API来实现动画的正播和倒播。

在这里插入图片描述



一、创建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();
    }
}





TechX —— 心探索、心进取!

每一次跌倒都是一次成长

每一次努力都是一次进步

END
感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何问题或意见,或者想要了解更多关于本主题的信息,欢迎在评论区留言与我交流。我会非常乐意与大家讨论和分享更多有趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!

相关推荐

  1. 原理使用

    2024-04-12 09:48:01       39 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-12 09:48:01       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-12 09:48:01       20 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-12 09:48:01       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-12 09:48:01       20 阅读

热门阅读

  1. 日志数据采集存储

    2024-04-12 09:48:01       15 阅读
  2. 提升写作效率:掌握ChatGPT论文写作技巧

    2024-04-12 09:48:01       18 阅读
  3. 数据库:SQL分类之DDL详解

    2024-04-12 09:48:01       19 阅读
  4. 无人机三维建模对光伏测绘的影响有多大?

    2024-04-12 09:48:01       16 阅读
  5. WPF 编辑器模式中隐藏/显示该元素

    2024-04-12 09:48:01       16 阅读
  6. 程序“猿”初学者学习计划

    2024-04-12 09:48:01       51 阅读
  7. 基础算法学习笔记(C++)

    2024-04-12 09:48:01       55 阅读
  8. 支持向量机和感知机有什么关联?

    2024-04-12 09:48:01       48 阅读