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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java基于微信小程序的在线投稿小程序(V2.0),附源码

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

深入浅出 -- 系统架构之日均亿级吞吐量的网关架构(DNS轮询解析)

在前篇关于《Nginx》的文章中曾经提到&#xff1a;单节点的Nginx在经过调优后&#xff0c;可承载5W左右的并发量&#xff0c;同时为确保Nginx的高可用&#xff0c;在文中也结合了Keepalived对其实现了程序宕机重启、主机下线从机顶替等功能。 但就算实现了高可用的Nginx依旧存在…

vivado 在硬件中调试逻辑设计

在硬件中调试逻辑设计 设计中包含调试核后 &#xff0c; 您可使用运行时间逻辑分析器功能来对硬件中的设计进行调试。 使用 Vivado Logic Analyzer 进行设计调试 Vivado Logic Analyzer 功能可用于与设计中运行的新 ILA 、 VIO 和 JTAG-to-AXI Master 调试核进行交互。…

LRU缓存结构【C语言】

#include <stdio.h> #include <stdlib.h>//双链表节点结构 typedef struct Node {int key;int value;struct Node* pre;struct Node* next; } Node;//LRU结构 typedef struct {int capacity;struct Node* head;struct Node* tail;struct Node** cache; }LRUCache;…

TCP/IP 协议栈在 Linux 内核中的 运行时序分析

1、Linux内核概述 1.1 Linux内核结构 一个完整的Linux内核一般由5部分组成&#xff0c;它们分别是内存管理、进程管理、进程间通信、bai虚拟文件系统和网络接口。 1、内存管理 内存管理主要完成的是如何合理有效地管理整个系统的物理内存&#xff0c;同时快速响应内核各个子…

顶顶通呼叫中心中间件(mod_cti基于FreeSWITCH)-回铃音补偿

文章目录 前言联系我们解决问题操作步骤 前言 回铃音&#xff1a; 当别人打电话给你时&#xff0c;你的电话响铃了&#xff0c;而他听到的声音叫做回铃音。回铃音是被叫方向主叫方传送&#xff0c;也是彩铃功能的基础。我们平时打电话听到的“嘟 嘟 嘟 嘟”的声音&#xff0c;就…

4-云原生监控体系-Grafana-基本使用

1. 介绍 使用Grafana&#xff0c;您可以通过漂亮、灵活的仪表板创建、探索和共享所有数据。查询、可视化、提醒和理解您的数据&#xff0c;无论数据存储在何处。 图片出处&#xff1a; https://grafana.com/grafana/ 官方网站 2. 界面介绍 Connections 可以配置数据源&#x…

物联网实战--驱动篇之(七)RTC时钟(DS1302)

目录 一、RTC简介 二、DS1302介绍 三、初始化 四、字节读写 五、功能函数 一、RTC简介 实时时钟&#xff0c;简称RTC&#xff0c;这个在STM32的外设里也有&#xff0c;不过STM32F1系列的RTC实际上只有一个计数器功能&#xff0c;如果需要年月日要自己写软件计算 &#xff…

【应用】SpringBoot-自动配置原理

前言 本文简要介绍SpringBoot的自动配置原理。 本文讲述的SpringBoot版本为&#xff1a;3.1.2。 前置知识 在看原理介绍之前&#xff0c;需要知道Import注解的作用&#xff1a; 可以导入Configuration注解的配置类、声明Bean注解的bean方法&#xff1b;可以导入ImportSele…

MySQL——全文检索

不是所有的数据表都支持全文检索 MySQL支持多种底层数据库引擎&#xff0c;但是并非所有的引擎支持全文检索 &#xff0c;目前最常用引擎是是MyISAM和InnoDB&#xff1b;前者支持全文检索&#xff0c;后者不支持。 booolean模式操作符 实验&#xff1a; 表productnotes &…

LVGL9.1移植STM32F103C8T6花屏问题解决

这一次的话算是花了一下午差不多解决了一个问题&#xff0c;具体我是用 stm32f103c8t6(20k RAM, 128k Flash) 移植的LVGL库(屏幕是240x240的st7789, 因为RAM的buf不太够所以缩小了显示面积) 直接切入主题: 如果出现花屏问题&#xff0c; 这个问题出在你自定义编写的lv_set_flu…

Ingress配置优化和追踪

介绍 在传统的业务系统中&#xff0c;应用微服务化后&#xff0c;需要一个统一的入口来将各个服务进行整合&#xff0c;这个入口可以是Nginx、Apache、HAproxy等等。而在K8s中&#xff0c;同样需要一个工具来将应用的各个service整合到统一的入口&#xff0c;这个工具就叫Ingr…

计算机网络 虚拟局域网划分

一、实验内容 1、分别把交换机命名为SWA、SWB 2、划分虚拟局域网 valn &#xff0c;并将端口静态划分到 vlan 中 划分vlan 方法一&#xff1a;在全局模式下划分vlan&#xff0c;在SWA交换机上创建三个vlan&#xff0c;分别为vlan2&#xff0c;vlan3&#xff0c;vlan4。 方…

C++/QT 医院信息管理系统

一、项目介绍 &#xff08;1&#xff09;管理员、居民、医生三个角色登录&#xff1b;居民可注册账号登录&#xff0c;医生由管理员添加&#xff0c;管理员权限最高 &#xff08;2&#xff09;管理员&#xff1a; 模块一&#xff1a;信息管理&#xff08;医生信息管理、医院…

VUE typescript 调用stompjs[Rabbit MQ]

npm拉下来最新的2.3.9版本&#xff0c;发现一些原来Js代码已经不能用了。顺便解读了下最新定义的内容 // <reference types"node" />export const VERSIONS: {V1_0: string;V1_1: string;V1_2: string;supportedVersions: () > string[]; };export class C…

kail渗透工具之nmap的使用方法

准备工作&#xff1a;开启两台虚拟机和一台Windows主机 kail Linux攻击机&#xff1a;192.168.80.131 red hat靶机&#xff1a;192.168.80.129 Windows主机&#xff1a;192.168.252.42 1、nmap扫描工具的简介 nmap是用来探测计算机网络上的主机和服务的一种安全扫描器。为了绘…

蓝桥杯嵌入式(G431)备赛笔记——第十一届第二场真题

关键代码&#xff1a;、 user.c: u32 adc_tick 0; // 定义一个无符号32位整型变量 adc_tick&#xff0c;用于记录上次ADC处理的时间戳 u32 r37_value 0; // 定义一个无符号32位整型变量 r37_value&#xff0c;用于存储ADC通道2的采样值 u32 r38_value 0; // 定义一个无符号…

用Python给PDF文档设置背景色或背景图

PDF作为一种跨平台、高保真的文件格式被广泛应用&#xff0c;尤其在报告、手册、电子书、合同等场景中&#xff0c;其重要性不言而喻。然而&#xff0c;在满足基本内容展示需求的同时&#xff0c;为了增强视觉效果&#xff0c;提升阅读体验&#xff0c;或者出于品牌标识、企业形…

IDE Eval Reset —— idea 重置试用期插件安装

idea 重置试用期插件安装 一、在线安装&#xff1a; 1、打开IntelliJ IDEA 2、file—> setting —> plugins 添加三方插件库 点击后&#xff0c;跳出弹框点击号&#xff0c;添加图中的网址 https://plugins.zhile.io3、搜索 IDE Eval Reset &#xff0c;安装插件 4…

东用科技助力5G+区域教育管理智慧平安校园建设

一、 方案背景 为深入贯彻党中央、国务院关于加快5G发展、加强教育信息化工作的决策部署&#xff0c;加快推进《5G应用“扬帆”行动计划》实施&#xff0c;促进5G与教育融合创新发展&#xff0c;按照“育人为本、多方协同、问题导向、深度融合”的原则&#xff0c;工业和信息化…