首页 > 其他分享 >Unity Animation动画系统概述

Unity Animation动画系统概述

时间:2024-02-05 12:33:48浏览次数:19  
标签:动画 Clip unity 状态机 Unity Animation Animator

一、Unity Animation动画系统基本介绍

unity提供了一套非常强大灵活且成熟的动画系统,不论是2d还是3d动画都有相应的组件和接口提供给开发者使用,不过这篇文章主要还是讲解3D部分的动画系统。我们在游戏开发时时常需要角色动起来,除了位置上的移动之外,我们还需要匹配角色的行为或玩家的输入播放不同的动画,不同的动画之间又需要平滑的衔接,有时我们还需要将两个动画混合、覆盖,动画复用等等,以上提到的种种操作在unity中都可以通过几个unity的组件实现:Animation Clip、Animator Controller、Avatar。

笼统的介绍一下这三个组件,Animation Clip顾名思义是一个个动画片段,如何理解动画片段呢,在unity中,动画片段记录了对应的模型上各个部分例如左大臂、左手小拇指的第二个指节或者一些简单的物体如门、窗的位置、旋转、缩放数据,而我们unity中的动画就是模型中各个部分的位置、旋转、缩放随时间变换组成的。

Animator Controller则像是可以自定义规则的容器,我们将某个角色要的Animation Clip放入Animator Controller中,这个时候我们就可以按照我们的需要来规定每个Animation Clip应该是什么时候以什么方式播放。一句话,Animator Controller不生产动画,它只是动画的搬运工,按照我们的规则将Animation Clip在合适的时间以合适的方式在模型上展现出来。

而这个Avatar就比较特殊了,Avatar所针对的只是人形的动画,并且Avatar起到的是一个翻译官的作用,他被用来在两个动画骨骼不相同的模型之间匹配动画以达到复用动画的作用,其原理笼统的来讲,就是unity自身提供了一套标准的蒙皮动画骨骼,Avatar将A模型的动画以unity标准的格式记录下来然后在以Avatar为骨骼操纵B模型播放A模型的动画。接下来我们就系统的讲解一下这三个unity的动画系统组件。

二、Animation Clip:动画切片

Animation Clip动画切片是Unity动画的原材料,任何unity动画都是一个或多个动画片段拼接组合起来的。而Animation Clip可以在Unity编辑器内使用Animation Window创建,也可以从外部导入如导入FBX。如果需要的话,从外部导入的单个动画切片也可以通过切分再次分为几个不同的动画切片。
image
上图为从外部导入的Animation Clip的inspector视图

需要注意的是,从外部导入的动画不能在unity的animation window 中编辑,但可以在animation window中查看,也就是外部导入的animation数据是只读的(read only)。更多的关于这个界面的介绍参考官方文档

就如概述中所说,Animation Clip记录的是对应物体各个部分的位置、旋转、缩放随时间变换的信息。

image
我们拿上面这个在Unity中制作的Box Bounce动画来举例子,可以看到,这个动画片段包含了位置、旋转、缩放的信息,在Animation视图中这些信息以动画曲线的方式展现出来,当然还有以关键帧的形式展现,但最终的动画时依照动画曲线来运动的。当我们播放这个动画片段的时候,这个box就会按照我们的曲线在不同时间段的值来改变相应的数值从而让我们的Box动起来。

这个时候我们就要问:只有这个box能播放这个动画片段吗?既然我都这样说了,那么说明其它的模型也是可以播放这个动画片段的。为什么?我们再次看这一段话“Animation Clip记录了gameobject及其子物体的位置、旋转、缩放随时间变换的信息”,是的,所有拥有transform组件的gameobject都可以播放这个动画片段。当然也不是所有的动画片段都是这样,上述说明的只是其中最简单的一种情况,一些拥有子物体动画的Animation Clip就不一定能够在其他gameobject上播放了,再比如骨骼动画就不能,一个骨骼动画只能有对应骨骼的模型才能播放,这一点也涉及到了之后我们要讲的Avatar组件,后面我们在解释为什么,这里暂且按下不表,让我们先再深入一点了解animation clip文件。

Animation Clip究竟是以何种方式记录动画信息的呢?让我们看一张截图:

image

上面这个截图就是记录了Box Bounce动画的Animation Clip文件,文件以anim后缀存储,用标记语言YAML编写。让我们仔细看一下这个文件的第九行,有一个字段叫m_Name,值为box_Bouncing正是unity中显示的animation clip的名称。再往下看,第16行、78行、185行,分别为m_EulerCurves、m_PositionCurves、m_ScaleCurves,显然这几个字段就记录了动画的旋转、位置、缩放信息,并且从m_EulerCurves字段下的m_Curves的六个子字段可以看出其正是对应了旋转动画的六个关键帧。值得一提的是,所有unity动画的信息都是以曲线的方式记录的。这也不难理解为什么其他的gameobject也可以播放这个动画片段。

image

上图就是一个人类模型正在播放boxBounce的动画,模型在这一刻依照动画曲线的值被压缩了。

可以说,boxBounce这个动画片段可以用在任何gameobject上,但不是所有的Animation Clip都能够在任何gameobject上播放,正如我们前面所说的,一些拥有子物体动画的Animation Clip就不能在任意的gameobject上播放了,下面让我们看一个双开门开门的动画,这个取名为door的gameobject拥有两个子物体分别是左边的门left和右边的门right。

image

在Animation View的曲线视图中我们可以看到两个子物体left和right都有对应的动画曲线也就是两个子物体都有动画,当我们想在其他的gameobject上比如我们之前使用的Box上使用这个Animation Clip的时候就会发现没有任何反应,问题出在哪里?很显然box这个gameobject并没有任何的子物体,也自然不能播放这个动画片段,那么现在我们在创建一个与这个双开门层级结构相同的gameobject,我们把两个box放下一个空gameobject下,一个取名CubeLeft一个取名CubeRight,然后我们再让这个gameobject播放双开门的动画试一下。

image

可以很直观的看到,这个gameobject没有成功播放我们双开门的动画,即使它的层级结构是一样的,问题出在哪里呢,我们看Animation View中那些发黄的字段后面有”Missing“的提示,这表示unity没有找到left和right的信息,现在我们有眉目了,如果我们把两个子物体的名称改成一样的试试看呢?

image

非常好,当我们把两个box的名称改成双开门动画切片所记录的left和right后动画被播放了,现在我们可以说,只要父子级层级结构和名称相同,动画片段就可以被复用。其中的原因让我们在记录动画片段数据的anim文件中寻找。我们同时打开boxBounce和doorOpen的动画片段文件作比较。

image

可以看到在第16行记录旋转信息的m_EulerCurves字段,box有一个curves字段而door有两个,这很好理解,因为door有两个子物体,两个子物体都分别有旋转动画,并且door的position和scale都没有动画所以两个记录位置信息和缩放信息的m_PositionCurves和m_ScalerCurves字段都没有curves信息。现在让我们展开m_EulerCurves字段的第一个Curves字段再做比较。

image

可以看到,因为两个动画的关键帧不同,m_Curves字段记录的数量也不同,对应于box动画的6个关键帧,和door动画的2个关键帧,具体的曲线数据被记录在serializedVersion字段下,这里我们不展开讨论。除了关键帧数量不同外,我们还可以看到最后一个path字段,这里正是动画能否被正确播放的关键,box动画中的path字段没有记录信息,而door动画中的path字段记录了一个left信息,这里我们就可以大胆猜测,这里的path信息就记录了当前的curves所记录的动画信息是属于哪一个子物体的,让我们来进一步验证一下。

image

我们构造了这样一个gameobject,这个gameobject包含两层父子级结构,e是b的子物体,b是y的子物体,在动画编辑器中我们给e这个孙级添加了动画,现在让我们看看在anim的文件当中是否也记录了这样的层级关系以用来确定动画数据的归属。

image

正如我们所想的那样,path这个字段就记录了这样的层级关系,这告诉unity“上述动画数据是属于子物体b下的子物体e的”,这样动画就能被正确播放了。同理,任何一个gameobject只要有符合这样的层级关系并且名称也相同,那么这个gameobject就可以正确播放这个Animation Clip了。

现在我们反过来想一想path字段为空的情况意味着什么,意味着当前动画数据就是属于这个物体本身的,拿boxbounce的动画来说,为什么其能够在所有的gameobject上被播放?因为不论是有子级还是没有子级或者本身就是子级等等情况都不重要,path字段为空就意味着,当前播放的这个动画只会影响到自身,而其子物体也会随着父级物体变换而变换。

那么到这里我们就对Animation Clip从表面到底层有了一个粗浅的了解,还有一些内容我们放到之后讲Animator Controller和Avatar的时候在讲,接下来我们进入unity动画系统的重中之重,Animator Controller动画控制器。

三、Animator Controller:动画控制器

Animator Controller的作用是将一堆Animation Clip用动画状态机(Animation State Machine)的方式组织起来,按照我们设定的条件、方式去选择、处理并播放动画切片。Animation Controller的核心之处就在于动画状态机,其通过类似于流程图的方式管理动画在各个状态之间切换。同时Animation Controller文件以controller为后缀同样是用标记语言YAML所编写。

image

上图就是Unity的Animator Controller的界面。

Animator Controller的界面分为了两个主要部分,一个是左边的图层(Layers)和参数(Parameters),一个是右边的可视化的动画状态机编辑界面。我们先从动画状态机讲起。

动画状态机也是State Machine(状态机)的一种,状态机的一些概念我们暂且不提,这里我们就了解一下unity动画状态机的构成部分,包括一个状态集合(Animation Clip集合),当前状态(当前播放的Animation Clip),和各个状态之间的转换关系和转换条件的集合(Transitions and Parameters)。

Animation Parameters动画参数,这个是控制Animation State Machine从一个State转移到另一个State的关键,几乎所有对Animation State Machine 的当前状态任何的修改都要通过改变Animation Parameter的值来达到目的。Animation Parameter可以在Animator 的Parameters面板中定义,一共有四种参数类型,int、float、bool、trigger,并且可以通过脚本在代码中访问并修改Parameter的值。

image

实现状态之间的转换除了转换的条件之外unity还需要知道转换应该从哪里开始从哪里结束,这个时候就可以通过State Machine Transitions(状态机过渡)来表示状态之间的转换关系,在Animator Controller的可视化状态机视图中,这种关系以单箭头的方式表现出来。

image

由状态机过渡链接的两个状态在满足转换条件时会依照箭头的朝向从一个状态转移到另一个相连接的状态,如果没有任何的转换条件,当上一个动画播放完成时会自动向所链接的下一个状态转换,并且,转换可以不是瞬时发生的,在Transition的设置中我们可以设置两个动画转换时平滑的混合过渡和转化发生的时间等等。

image

上图就是Transition的Inspector面板,在最下方我们可以看到添加转换条件的选线Conditions。

现在让我们回到动画状态机的“状态”上,在动画状态机中,状态一词指代的是一个动画片段,但其实在unity的动画状态机中,状态可以为空,也就是这个状态上没有动画片段,说到底,状态和最后呈现的动画没有必然联系,相同的动画状态机可以有不同的动画表现,这完全取决于开发者的设置,这样状态机相同,但表现的动画不同的设计可以实现一种动作的不同状态,例如正常的奔跑和受伤后的奔跑,都是处于奔跑的状态但是表现出的动画确实不同的,unity中也提供了让这一设想实现的功能,Animation Layers。

image

我们可以在Animator Controller的Layers面板中新增Animation Layer。

当然,Animation Layers不止有这一个功能,其更多被用来实现动画之间的组合,具体来讲就是两个不同的Animation Layer上的动画进行添加(additive)或覆盖(override),具体操作的部分可以通过Mask来确定。

image
image

如上图,Mask接受一个Avatar,由Avatar给出要添加或覆盖的部分,使用了Avatar也就默认只能混合人形动画了。unity会根据Avatar给出的部分去覆盖优先级较低的layer上的动画,而layer的优先级由在视图中的高低排列顺序决定。

image

Layer越靠后优先级越高,如上图所示,Layer1的优先级大于Layer0。

需要注意的是,当有多个layer存在时,优先级高的layer的动画会始终依照我们的设置覆盖或叠加低优先级layer的动画,如果我们想控制动画覆盖或叠加的时机,我们可以通过在高优先的layer的enter状态和实际动画状态之间添加一个没有任何动画片段的空状态来避免对低优先级layer动画的叠加或覆盖,这样我们就可以手动控制只在我们需要的时候让动画叠加或覆盖。

image

如上图,Layer1的empty状态的动画片段为空,当Layer1的动画状态处于empty时,是不会对Layer0的动画产生任何影响的,只有layer1处于animation状态时才会对layer0产生影响,但这个过程是受我们控制的。

Animation Layer已经讲了大部分了,其中还有一些例如在开头提到的状态机相同但动画不同的效果我们可以通过Animation Layer syncing来实现。更具体的信息可以参考unity官方文档和观看这个视频

接下来我们要讲一讲子状态机(Sub-State Machine)和混合树(Blend Tree)。

子状态机很好理解,可以想象成将几个状态折叠成一个状态。在transition连接到或从子状态机连接出去的时候也是要确定要连接到子状态机当中的哪一个状态,或由子状态机中哪个状态连接出来。

image

image
子状态机中的“(up)Base Layer”就是上一层的动画状态机的抽象表示,在子状态机连接出去时,也会让我们选择要连接的上一层动画状态机的状态。如下图所示。

image

接下来我们来聊一聊Blend Tree混合树(内容太多了 直接看视频和官方文档吧)
不过总的来说就是在不同动画之间做过渡混合。

四、Animator组件

如果一个gameobject想要播放相应的动画,就要挂载Animator组件来执行我们已经准备好的Animator Controller和Avatar的动画和功能。Animator组件相较于Animation Clip和Animator Controller这些偏理论的部分,Animator组件就偏实际应用方面,在这里我们就一个个过一遍Animator组件上的各个属性。

image

Controller:这里放置需要的Animator Controller。

Avatar:这里放置已经设置好的对应模型的Avatar,如果骨骼动画是通过Avatar进行传递的话,这里缺失Avatar模型动画将不能被正常播放。
Apply Root Motion:是否应用Root Motion根运动。

Updata Mode:动画更新的模式。也就是计算动画变换的模式。

Normal:更新频率与updata同步,也就是帧率有多少帧,动画就计算那么多次。

Animate Physics:更新频率与FixedUpdata同步,即与Unity的物理引擎同步,没做一次碰撞检测动画就计算一次更新。

Unscaled Time:与Normal模式相同,但不同的地方在于,这个模式下会忽略Time Scale时间缩放。

Culling Mode:剔除模式,类似于渲染当中的剔除,当动画处于摄像机视图外时进行剔除处理。

Always Animate:不参与剔除,无论是否在摄像机视图内。

Cull Updata Transform:同样是动画不参与剔除,但是ik会在动画处于摄像机视图外时被禁用。

Cull Completely:完全参与剔除,即动画在摄像机视图外时就停止。

除上述属性外,我们还可以看到在最下方还有一些关于当前Animator组件的信息,具体信息可以参看官方文档。

标签:动画,Clip,unity,状态机,Unity,Animation,Animator
From: https://www.cnblogs.com/HalfDog/p/18007606

相关文章

  • Unity 基于群体寻路的解决方案
    群体寻路是一种模拟群体行为的技术,它使得游戏中的角色能够以一种有组织的方式移动。在群体中,每个角色都有自己的目标位置,并且会根据周围的情况决定如何移动。群体寻路可以帮助我们实现一些有趣的场景,比如鸟群飞行、鱼群游动等。在Unity中实现群体寻路,我们可以使用NavMeshAgent组......
  • Unity基于C#事件委托机制
    事件委托是一种用于实现观察者模式的设计模式,它允许对象在发生特定事件时通知其他对象。在Unity中,事件委托机制为开发者提供了一种简单而有效的方式来处理游戏中的事件和交互。一、事件委托的基本概念事件委托是一种特殊的类型,它可以持有一个或多个方法的引用。当某个事件发生时......
  • 视野修炼第72期 | 22 种方式实现弹 动画
    欢迎来到第72期的【视野修炼-技术周刊】,下面是本期的精选内容简介......
  • 在Unity中快速生成基于模板的Lua脚本
    在学习Xlua时,这个工具提供了一个简单而强大的方式来快速生成基于模板的Lua脚本,有助于提高开发效率和保持代码的一致性。1.xlua框架导入在GitHub上搜索xlua,找到腾讯的xlua项目,下载项目的压缩包。然后将压缩包里的Plugins和XLua这两个文件夹导入Unity的Assets目录下,如下图所示:2.......
  • unity数据持久化-json
    JsonUtlityJsonUtlity是unity自带的序列化和反序列化工具类,主要提供了两个方法ToJson和FromJsonToJson序列化比如我们有一个类是这样的classPerson1{publicinttestI;publicfloattestF;publicdoubletestD;publicstringtestS;publicint[]......
  • 「效果图渲染」效果图与3D影视动画渲染区别
    效果图和3D动画渲染是图像渲染技术在各自领域的应用。效果图渲染常见于建造、室内布局以及产品设计等专业领域,其中对图像的准确性与细致程度有着较高的标准。相较之下,3D动画渲染更广泛地被运用于制作电影、电视节目、电子游戏和广告等娱乐媒介,不仅要处理更为动态和复杂的场景,还要......
  • ThreeJs实现简单的动画
    上一节实现可用鼠标控制相机的方式实现动态效果,但很多时候是需要场景自己产恒动态效果,而不是通过鼠标拖动,此时引入一个requestAnimationFrame方法,它实际上是通过定时任务的方式,每隔一点时间改变场景中内容后重新渲染一遍,间隔时间短的话视觉上就显示出连续的动画效果,Js本身也自带定......
  • react 点击按钮 div隐藏显示 添加展开收起动画效果
    js代码const[collapse,setCollapse]=useState(false)  const[showBack,setShowBack]=useState(false)  constchangeCollapse=()=>{//获取展开收起目标元素    constheaderDes=document.querySelector('.phone_header_des');  ......
  • 热门动画插件DoTween的使用
    做游戏基本都会用到插件,它能让你的效率更高,因为它里面都是写好了的方法,只要用就可以了。在众多动画插件中,DoTween脱颖而出,因为它用起来很简单也很方便,自然用的人就多了。那么我们也要学习下这个插件了。毕竟工作中用的还是蛮频繁的。你想想,你的UI动画,你难道要自己做动画吗?比如游戏......
  • 比较以下Unity AStar Pathfinding, NavMesh, Recast Navigation 寻路算法的优点与缺点
    一、AStarPathfindingAStarPathfinding是一种基于图搜索的寻路算法,它使用启发式搜索来找到最短路径。AStarPathfinding的优点包括:高效性:AStarPathfinding是一种高效的寻路算法,因为它使用启发式搜索来找到最短路径,可以大大减少搜索空间,从而提高寻路速度。灵活性:AStarPathf......