• Unity Playable动画系统取代蜘蛛网Animator


    入门视频:

    https://www.bilibili.com/video/BV1SP4y177YQ

    《永劫无间》的动作与运动系统

    https://www.bilibili.com/video/BV1QN411f7WJ?t=197

    相关文章

    https://zhuanlan.zhihu.com/p/386710404

    Playable API

    简单的说,Playable可以通过一组API来创建一个Graph,而每个Graph可以由多个树形结构组成,每个树状结构都由一个Output节点作为根节点,叶子结点则由各种Playable组成。

     

    它提供了一种创建工具,动画系统或其它游戏机制的方式。Playable Graph允许我们混合和修改多个数据源,并通过单个输出来播放它们。

     

    目前Playable API 支持播放动画、音效以及自定义的行为。我们把它设计为了一种通用的接口,它并不是仅仅针对动画的。以后还会支持视频等其它系统。

     

    图片

    为什么要开发这么一套API

    在我们技术支持团队平时接触客户时发现,由于历史原因或者是出于习惯,依然有很多开发者在使用Legacy动画系统Animation组件。Legacy动画系统对于程序来说是比较直观的,但这就意味着没办法使用Mecanim动画系统的一些高级功能,例如:动画重定向、Blend Tree等。

     

    首先,设计Playable API的一个目的就是为了代替Legacy动画系统。

     

    开发人员可以用他们喜欢的方式来直接控制动画系统,而不是必须使用Mecanim系统的动画状态机来驱动动画系统。在Unity底层,驱动Playable Graph的实际上依然是Animator组件,所以在使用时需要你传一个Animator组件给Playable Graph。但是你完全可以像使用Animation组件一样使用Playable。

     

    其次,我们还可以定制适配于自己项目的动画系统,而不用必须使用动画状态机来构建我们的动画逻辑。

     

    这是一个非常有用,也非常有挑战性的工作。例如:有些有技术积累的公司,他们已经开发过自己的动画控制系统,这里不是指底层的动画驱动系统,而是更高层的动画控制系统,这套系统可能是架构在其它引擎上或是自研引擎上的。如果它们想要沿用这套系统的话,就可以用Playable来更方便的重构他们的控制系统,来适配到Unity上。更重要的是,我们可以定制更加适配于具体项目的动画系统。

     

    再者,Playable API可以更直接的访问底层动画系统的接口。

     

    最后,我们可以通过Playable来扩展Timeline的功能。

     

    图片

    使用Playable有什么好处

    首先Playable结构简单。它不会有庞大的、蜘蛛网一般的状态机。如果只是单纯播放动画的话,可能几句话就够了,使用起来就像Legacy Animation组件一样。如果是大型的RPG或FPS游戏,我们也没必要把大量的动画都添加到Graph中,我们可以预先创建好需要的子树,然后根据需要在添加到Graph中。

     

    动画状态机是不允许运行时添加、删除动画的,它只能使用OverrideController来替换动画。这其实在开发中时是不太方便的。因为当动画数量非常多的时候,我们又没办法在运行时添加、删除State,这往往造成会生成一个巨大的状态机来满足所有可能的状态,而这个巨大的状态机维护起来是十分麻烦的。

     

    在Animator状态机中,是通过定义变量来间接控制权重的。而在Playable中,你可以直接控制动画的权重和时间。例如:我们可以让二个动画按0.2和0.8的权重混合。Playable的这种方式是更加灵活的,但是相对的它对于普通开发者实际上是没有Animator parameter方便的。

     

    我们不仅能在二个Animation clip之间混合,我们也可以在Clip和AnimatorController之间混合,甚至无数个AnimatorController之间混合都是可以的。这就给我们带来了更多的便利:在动画状态机加这个系统中,二个State machine之间是不能过度的,但是如果使用了Playable,那就是可行的。所以我们完全可以混用Animator状态机和Playable。一些角色的固定动画状态的转变我们可以继续用动画状态机,而那些需要动态改变的功能我们就用 Playable。

     

    图片

    基本架构

    Playable API大体上有二部分组成:各种Playable和PlayableOutput。

     

    Playable是我们这套API的基本组成元素。为了避免Gc alloc,所有的Playable是用Struct实现的。PlayableOutput是每个Graph必须的组成元素。而且PlayableOutput必须连接到至少一个Playable上,否则它是没有任何作用的。

     

    图片

     

    注意:上图中所列的Playable结构是不全的,因为后续版本还会不断有新的Playable被添加进来。

     

    Playable Graph

     让我们先从图形入手,对Playable Graph有一个直观的认识。下图是一个很简单的Playable Graph。

     

    图片

     

    我们可以看到这里有二个节点:一个Animation clip节点和一个Output节点。这里实现的功能很简单,就是播放一个动画。这是一个Playable节点形成的一个非常简单的树形结构,基本上,每一个PlayableOutput都会形成一颗树形结构。

     

    让我们来看一个稍微复杂点的例子。

     

    图片

     

    一个树形结构一般由一个输出节点,若干个输入节点以及若干个功能节点组成。在上图中,有一个输出节点Animation Output,二个输入节点Animation clip以及二个功能节点。这个Graph实现的是二个动画作为2个Layer来融合,可以看作和动画状态机中的Layer是一样的功能。

     

    如果单看动画这一部分的话,Playable graph本质上就是一颗动画混合树。注意:这里指的这个混合树并不是Blend Tree,而是一个更广泛的概念。它的内部节点是运算符,主要起到混合的作用,而叶节点是输入。

     

    现在,我们来看一个更复杂一点的例子。

     

    图片

     

    这是由三颗树形结构组成的一个Playable Graph。它由Animation output、Audio output以及Script output 三个输出节点。可见一个Playable Graph不仅仅可以包含动画,它是一个更广泛的概念,你可以在播放动画的时候同时播放音效以及自定义的逻辑。

     

    Playable用途

    通过以上的介绍,大家应该能对Playable Graph有一个直观的、简单的认识。那么Playable又能实现哪些功能呢?接下来我们就深入了解一下。

      

    首先,我们可以用Playable API代替Animation组件。这使得我们在满足Legacy动画系统的机制的同时,我们还可以获得其它好处,例如:Blend Tree、动画重定向等。

     

    这里给出一个官方的示例,主要用Playable API来模拟了一套动画状态机,以类似Animator动画状态机的机制来使用Playable,大家可以参考一下。

     

    官方的示例下载:

    https://github.com/Unity-Technologies/SimpleAnimation

     

    再就是按照项目的需求来定制了。你可能并不喜欢用动画状态机来管理动画,也有可能你想要实现一个自动评估transition的系统,使系统可以评估当前状态,自动过渡到最理想的动画等等。Playable API是提供给了大家按需定制的功能。

     

    图片

    运行时创建

    我们的Playable Graph都需要在运行时创建,目前它还没有一个默认的Asset来供使用。不过开发者完全可以自己来创建自己的Asset来序列化自己想要的Graph结构,然后运行时通过Asset来反序列为Graph。

     

    下面是创建一个简单的Graph的代码。

     

    图片

     

    从上图可以看到,Playable结构都是以工厂方法Create来创建的。通过这段代码创建的Graph就是下图这样的,十分简单。

     

    图片

    运行时添加

    在运行时,可以创建你想要的Playable并且连接到对应的输出节点或者另一个Playable上。

     

    图片

     

    在用工厂方法创建好AnimationClipPlayable结构后,需要通过Graph.Connect方法来连接到Graph中。

     

    下图就是这段代码添加二个Playable后的Graph的样子。

     

    图片

    运行时移除和销毁

     

     类似的,我们也可以在运行时动态修改Graph的结构,以及销毁指定的Playable。

     

    图片

    直接访问与控制Playable

     

    我们可以直接控制每一个Playable的属性,例如播放速度。我们甚至可以暂停一个动画把它当作一个Pose来使用。

     

    其次我们可以控制每一个Playable的混合权重,通过控制权重,我们就可以实现类似Blend Tree、Transition等功能。

     

    我们还可以控制更新频率,这在我们做动画的性能优化时比较常用,例如:那些距离Camera过远的角色我们就可以把动画的更新频率降低。

      

    要方便的控制每一个Playable还需要我们做一些额外的工作。这是因为:想象一颗非常深的树形结构,你想访问或控制非常深的一个叶节点实际上是不太方便的。当然你可以保存起它的引用,不过那通常不太灵活,像是写死在代码中一样,一个二个还好,但是你不可能把所有的都存下来。所以这就需要我们做一些额外的工作来管理和访问Playable。例如:提供一个查询叶子节点的接口或者实现一个类似Animator parameter系统来和Playable的属性进行映射。

     

    图片

    Transitions

     

    Playable API 中没有专用的Transition功能,它没有类似动画状态机中二个state之间拉一个线段来定义Transition。这是因为Transition本质是二个动画的Blend,所以Playable API没有提供专用的Transition功能,而是提供给应用层基本的Blend功能,应用层根据自己的需求来实现Transition。

     

    这样就给了我们更多的可能性。除了经常使用的平滑过渡以外,我们还可以实现其它类型的过渡,例如:冻结过渡,也就是由动画A过渡到动画B时,A就不在更新了。而动画B在更新的同时Weight由0变为1。这种过渡方式比较适合那些前后二个动画差异较大的情况。另外我们为了更圆滑的过渡,我们还可以控制过渡时的曲线,例如:使用Hermit曲线。

     

    这给我们带来了更佳灵活、自由的控制方式。现在的动画状态机使用起来虽然方便,仅在二个State之间拉一条线就可以了.但是我们想要精确的控制Transition却比较困难。例如:有这么个需求,服务器和客户端动画的同步问题。如果用Animator来做的话,是很难做到完美的。因为Transition你在外部是不能直接控制的。而如果用Playable的话,我们只需要同步二个动画的ID,以及它们的权重和Transition的持续时间就可以了。

     

    图片

    Animation Layer

     

    Playable API同样可以实现骨骼分部混合和加法混合,类似于Animator中的Layer功能。这在我们混合全局姿势和局部姿势时会非常有用。比如RPG中上下半身的动画,像是边跑边攻击之类,或者是FPS中角色边跑边换枪以及瞄准姿势等。我们也可以运行时动态的增加、删除Layer,如下图所示。

     

    图片

     

    通过设置AnimationLayerMixerPlayable的input weight来控制混合。一个基本的Animation layer混合如下图所示。

     

    图片

    Blend Tree

     

    我们也可以用Playable实现Blend Tree来混合动画。

     

    图片

    这段代码相当于实现了一个最简单的1D Blend Tree。

     

    图片

     

    通过AnimationMixerPlayable来进行混合,并且通过Input weight来控制混合过程。为了保证动画的准确性,AnimationMixerPlayable的混合权重在内部会保证和为1。

     

    虽然Playable可以定制Blend Tree的混合方式,但是个人觉得对于一般的开发者来说目前过于繁琐了。比如2D Blend Tree中的三元混合,你需要以三角形质心坐标系来计算三个输入的权重。因此对于Blend Tree,大家不如直接使用动画状态机中的Blend Tree,那样更为方便。

     

    与Animator Controller混合使用

     

    首先,Playable可以和Controller叠加分层动画。在动画状态机中Layer是Static的。所以利用Playable和Animator controller混合就可以起到动态添加你想要的Layer的作用。

     

    其次,Playable可以和Controller进行混合,你可以让它们按一定的权重进行Blend。

     

    再者,Playable可以和Controller互相CrossFade。例如:我们有一把武器,想要让武器来告诉角色该怎么使用这把武器。所以我们创建一个Animator controller放在武器上,当角色拿起武器后,就可以CrossFade到武器的动画状态机上。这可以让大大降低我们的动画系统的复杂度,因为动画的CrossFade不在局限于一个状态机里了。

     

    最后,二个Controller可以进行混合。例如:你可以从一个状态机Crossfade到另一个状态机上。

     

    图片

    控制多个输出

     

    一个Playable Graph中不是只能有一个Output。除了动画的Output,我们还能添加其它种类的Output,例如:Audio output或者Script output. 这给了我们同时播放不同种类的Playable的能力,我们可以在动画的某一时间点播放一个音效。

     

    图片

     

    上面段代码会生成下图的Graph。

     

    图片

    PlayableBehaviour

    PlayableBehaviour是一个供用户自定义行为的类。我们可以对Playable进行直接的访问和控制。同时它也定义了一些回调函数来捕捉一些事件。例如:开始播放时的事件、销毁事件,我们想监控这些事件时就离不开PlayableBehaviour这个类。

     

    更重要的是,它提供了一些在每一帧的动画计算流程上的回调。例如:PrepareFrame函数会在Prepare frame这个阶段回调。我们就可以在每一帧对Playable中的元素进行访问和设置,例如:Time和Weight。可以说这是在做自定义Blend中不可缺失的功能。

     

    举个例子:你想把子弹时间和人物射击动画的时间联系起来,你可以在PrepareFrame或ProcessFrame中获取射击动画的Normilized time,来修改游戏的Time.scale。

     

    图片

    PlayableGraph Visualizer

    PlayableGraph Visualizer是一个查看Playable graph结构的工具,这个工具目前来看还有些简单,它只能查看Graph的结构,没有编辑功能。但是对于Debug或者了解Playable graph的运行过程还是有帮助的。

     

    PlayableGraph Visualizer下载地址:

    https://github.com/UnityTech/graph-visualizer

     

    图片

    未来展望

    在Playable未来的开发中,我们会逐渐加入更多功能,例如: Procedural Animation与C# Job System相结合、Custom mixer、Motion stream等等。Procedural animation: IK, full body IK或者是FPS中举枪瞄准的动画。

     

    结合C# Job System,可以让你的Playable运行在一个高效的、线程安全的子线程中,而

    Custom mixer,可以定制你自己的mixe,Motion stream,一个特殊的Node可以直接播放一个Pose。

  • 相关阅读:
    【NOIP 2003】 加分二叉树
    【POJ 1655】 Balancing Act
    【HDU 3613】Best Reward
    【POJ 3461】 Oulipo
    【POJ 2752】 Seek the Name, Seek the Fame
    【POJ 1961】 Period
    【POJ 2406】 Power Strings
    BZOJ3028 食物(生成函数)
    BZOJ5372 PKUSC2018神仙的游戏(NTT)
    BZOJ4836 二元运算(分治FFT)
  • 原文地址:https://www.cnblogs.com/sanyejun/p/16194217.html
Copyright © 2020-2023  润新知