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

news2024/11/27 14:30:59

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

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

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

助力快速掌握 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();
    }
}





TechX —— 心探索、心进取!

每一次跌倒都是一次成长

每一次努力都是一次进步

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1587322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python-study-day1

ps&#xff1a;前言 可做毕设&#xff0c;html&#xff0c;web&#xff0c;app&#xff0c;小程序&#xff0c;bug修改&#xff0c;可加急 作者自述 作为一名前端开发工程师&#xff0c;这个大环境不好的情况下&#xff0c;我试过我前端接单子但是没有后端&#xff0c…

Leetcode 239. 滑动窗口最大值和Leetcode 347. 前 K 个高频元素

目录标题 Leetcode 239. 滑动窗口最大值题目描述C语言代码和题解解题思路 Leetcode 347. 前 K 个高频元素题目描述C语言题解和思路解题思路 Leetcode 239. 滑动窗口最大值 题目描述 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最…

Java基础07--多线程-网络编程-Java高级

一、多线程 1.认识多线程 ①线程 ②多线程 2.创建线程方式 ①方式一&#xff1a;继承Thread类 1.让子类继承Thread线程类 2.重写run方法&#xff0c;就是这个线程执行会执行的操作。 3.创建继承Thread的子类对象就代表一个线程 4.启动线程:.start()-自动执行run方法 注意&am…

微服务demo(四)nacosfeigngateway(2)gatewayspringsercurity

一、思路 1、整体思路 用户通过客户端访问项目时&#xff0c;前端项目会部署在nginx上&#xff0c;加载静态文件时直接从nginx上返回即可。当用户在客户端操作时&#xff0c;需要调用后端的一些服务接口。这些接口会通过Gateway网关&#xff0c;网关进行一定的处理&#xff0…

Spring Boot REST API - CRUD 操作

Spring Boot REST API - CRUD 操作 这里主要提一下 spring boot 创建 rest api&#xff0c;并对其进行 CRUD 操作 jackson & gson 目前浏览器和服务端主流的交互方式是使用 JSON(JavaScript Object Notation)&#xff0c;但是 JSON 没有办法直接和 Java 的 POJO 创建对应…

基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示汉字的功能

基于STC12C5A60S2系列1T 8051单片机的液晶显示器LCD1602显示汉字的功能 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍LCD1602字符型液晶显示器介绍一、LCD1602字符型…

20240411,内存分区模型

一&#xff0c;内存分区模型 生成可执行文件EXE文件之前代码区【函数体的二进制代码——共享-对于被频繁执行的程序&#xff0c;只需要在内存中有一份代码&#xff1f;&#xff0c;只读】&#xff0c;全局区【全局变量&#xff0c;静态变量&#xff0c;常量&#xff0c;该区域…

012:vue结合纯CSS实现蛇形流程图/步骤条

文章目录 1. 实现效果2. 实现代码 1. 实现效果 2. 实现代码 <template><div class"container"><div v-for"(item, index) in list" class"grid-item"><div class"step">step{{index1}}</div></div&…

计算机网络知识等汇总补充

计算机网络知识汇总补充 一、四次挥手1、为什么TCP要等待2MSL2、如果说一个系统中&#xff0c;有大量的time_wait和close_wait&#xff0c;会是什么原因&#xff1f; 二、你是怎么解决粘包问题&#xff1f;三、你觉得哪些场景适合redis四、redis的持久化策略五、你会怎么保证my…

视频基础学习六——视频编码基础三(h264框架配合图文+具体抓包分析 万字)

系列文章目录 视频基础学习一——色立体、三原色以及像素 视频基础学习二——图像深度与格式&#xff08;RGB与YUV&#xff09; 视频基础学习三——视频帧率、码率与分辨率 视频基础学习四——视频编码基础一&#xff08;冗余信息&#xff09; 视频基础学习五——视频编码基础…

WinRAR再爆0 day漏洞,0 day漏洞该如何有效预防

WinRAR再爆0 day漏洞&#xff0c;已被利用超过4个月。 Winrar是一款免费的主流压缩文件解压软件&#xff0c;支持绝大部分压缩文件格式的解压&#xff0c;全球用户量超过5亿。Group-IB研究人员在分析DarkMe恶意软件时发现WinRAR在处理ZIP文件格式时的一个漏洞&#xff0c;漏洞…

基于Springboot的笔记记录分享网站(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的笔记记录分享网站&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

牛客 NC36 在两个长度相等的排序数组中找到上中位数【中等 模拟 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/6fbe70f3a51d44fa9395cfc49694404f 思路 直接模拟2个数组有顺序放到一个数组中&#xff0c;然后返回中间的数参考答案java import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 pu…

使用htmlentities()和nl2br()将文本数据正确显示到前台

问题&#xff1a; 在后台textarea里编辑了有一串字符串&#xff0c;虽然在textarea里编辑是有换行效果的&#xff0c;但是数据获取到就只是\n&#xff0c;前端是不认识这个的&#xff0c;正确输出到前台的换行只能是<br/>。 $str "ABCDEFGHIJKLMNOPQ"; echo…

Multisim仿真二极管、晶体管和场效应管学习笔记

Multisim仿真二极管、晶体管和场效应管 &#xff08;note&#xff1a;使用Multisim14.0版本进行仿真&#xff09; 文章目录 Multisim仿真二极管、晶体管和场效应管二极管的I-V特性晶体管的I-V特性场效应管的I-V特性 二极管的I-V特性 插入I-V analyzer 原理图绘制 改变仿真…

【MCU开发规范】:MCU的性能测试

MCU的性能测试 前序性能评判方法MIPSCoreMark EEMBC其他参考 前序 我们平时做MCU开发时&#xff0c;前期硬件选型&#xff08;选那颗MCU&#xff09;基本由硬件工程师和架构决定&#xff0c;到软件开发时只是被动的开发一些具体功能&#xff0c;因此很少参与MCU的选型。 大部分…

面试经典算法系列之二叉树1 -- 从前序与中序遍历序列构造二叉树

面试经典算法16 - 从前序与中序遍历序列构造二叉树 LeetCode.105 公众号&#xff1a;阿Q技术站 问题描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根…

02 - Git 之命令 +

1 Git相关概念 1.1 以下所谈三个区&#xff0c;文件并不只是简单地在三个区转移&#xff0c;而是以复制副本的方式转移 使用 Git 管理的项目&#xff0c;拥有三个区域&#xff0c;分别是 Working area工作区&#xff08;亦称为 工作树Working Tree&#xff09;、stage area …

学习JavaEE的日子 Day33 File类,IO流

Day33 1.File类 File是文件和目录路径名的抽象表示 File类的对象可以表示文件&#xff1a;C:\Users\Desktop\hhy.txt File类的对象可以表示目录路径名&#xff1a;C:\Users\Desktop File只关注文件本身的信息&#xff08;文件名、是否可读、是否可写…&#xff09;&#xff0c…

简单了解JVM

一.JVM简介 jvm及Java virtual machineJava虚拟机&#xff0c;它是一个虚构出来的计算机&#xff0c;一种规范。其实抛开这么专业的句子不说&#xff0c;就知道 JVM 其实就类似于一台小电脑运行在 windows 或者 linux 这些操作系统环境下即可。它直接和操作系统进行交互&#…