Unity3D 中 Generic 动画导入设置和 Root Motion 之间的关系

2条评论

Unity3D 的 Mecanim 动画系统可以直接复用 3DS MAX 中制作的动画文件中的位移,这个就是通过 applyRootMotion 来达成的,我们只需要在使用 Animator 控制动画播放的同时,设置 Animator 的 applyRootMotion 字段为 True 就 OK 了。

那么怎么来利用这个特性达成我们想要的一些效果呢?这个 applyRootMotion 到底指的是啥呢?

ApplyRootMotion,从字面上理解来看,是『应用根节点的运动』,听起来貌似像那么一回事。可是我们可以从官方文档上看到这样一段话:

The Root Transform is a projection on the Y plane of the Body Transform and is computed at runtime. At every frame, a change in the Root Transform is computed. This change in transform is then applied to the Game Object to make it move.

翻译过来的意思,应该是这样的:

根节点的运动变换其实就是整个物体运动变换通过 Y 轴垂直在水平面上的一个投影。根节点的运动变换在动画的每一帧中都会进行计算。计算出来的根节点变换结果都会应用在播放动画的对象上,让该对象按照根节点的运动变换进行移动。

这段话大体的意思就是,RootMotion 这个玩意就是作用于动画物体在 X 轴和 Z 轴上的位移的,而且这个位移是根据实际播放的动画中每一帧物体的位移在 X 和 Z 轴上投影计算出来的。

这个特性非常赞特别是对于某些技能动画,整个动画是有一定位移的,但是动画的位移是动作设计师在设计时根据动作需要调出来的,位移是跟动作的幅度直接相关和匹配的。

那么在释放技能的时候就只需要直接播放动画,并且应用这个 Root Motion 的特性就可以很好的完成角色在播放攻击动作的同时进行移动,动作播放完毕之后就在动画结束帧角色所在的位置,切换为待机动作就 OK 了。

看起来很牛逼的样子对不对?是的,确实很牛逼。但是还有很多事情需要我们都一一了解以后,我们才能做出我们想要的东西的。

下面我们先岔开一下话题,好好说说这个 Animation Import Settings 中『Animations』Tab 页中各项设置的作用。

  • Import Animation,勾选这个才可以导入动画到 Unity 工程中;
  • Bake Animations,这个选项只在使用 Humanoid 动画并且使用到了 IK 特性的时候才可用;
  • Anim.Compression,这个是关于动画压缩选项的,默认会选择 Keyframe Reduction 这个是『压缩关键帧』,就是 Unity 会自行重采样动画的关键帧,还有两个选项『Off 和 Optimal』,一个是关闭动画压缩,一个是最优化压缩(应该是压缩效率最高,动画效果失真度可能也较高)
  • 选择了 Keyframe Reduction 或者 Optimal 压缩选项,就会有三个用于控制压缩选项的系数配置, Rotation Error,Position Error 和 Scale Error,这个三个参数默认都是 0.5,越小呢精度就越高也就是说动画的失真度越小。
  • Clips,这个下面列出了这个 FBX 文件下包含的所有动画,我们在默认的动画文件基础上新建和删除动画片段 (Animation Clip),当然每个动画片段都是可以指定起始帧和结束帧的; 以下的设置都是针对单个动画片段滴:
    • Loop Time,勾选这个选项之后,如果 Animator 处于播放这个动画状态时,在播放完第一遍这个动画片段之后,会自动循环从起始帧再次开始播放动画,如此循环往复。如果我们不勾选这个选项,例如 Animator 一直处于播放这个动画的状态,那么动画会定格在动画的结束帧,直到我们通过 Animator 切换这个 Animator 状态机的状态,切换到其他的动画;

      • Loop Pose 和 Cycle Offset,在勾选了 Loop Time 之后生效的两个选项,Loop Pose 用于控制动画循环播放时,从结束帧切换到起始帧时,动画的动作可以无缝的衔接上,Cycly Offset 就是用于控制循环的时候起始帧偏移用的;
    • Root Transform Rotation,根节点的旋转信息
      • Bake Into Pose,勾选后会将根节点每一帧的旋转方向信息烘焙到动画的骨骼运动中,在整个动画播放的过程中,根节点的旋转信息就不会在通过 Root Motion 作用到播放该动画的 GameObject 上了,这就意味着这个动画播放的过程中,该物体的 Transform 中的 Rotation 值不会因为动画中物体做了任何旋转而发生改变,而是会保持一个恒定的值,和该动画播放之前的旋转值保持一致;
      • Based Upon (at Start) 或者 Based Upon,根节点旋转的参考基准,有两个选项『Original 和 Root Node Rotation』这两个分别指的是动画文件中指定的旋转值和根节点旋转信息,其实我更愿意将 Original 理解为动画中原点的旋转值,因为在整个动画播放的过程中,所有骨骼肯定都会有旋转和位移的变换,但是动画的原点其实一定都是确定的,这样理解感觉更简单也更形象一些,勾选了 Bake Into Pose 之后,就会变成 Based Upon 而不勾选 Bake Into Pose 就会保持为 Based Upon (at Start),这个目前还木有理解为啥;
      • Offset,旋转角度与参考基准的偏移(以度为单位);
    • Root Transform Position(Y),根节点位移信息(Y 轴)
      • Bake Into Pose,勾选后会将根节点每一帧在垂直 Y 轴方向上的运动信息烘焙到动画的骨骼运动中,在整个动画播放的过程中,根节点在 Y 轴方向的所有位移信息不会通过 Root Motion 作用到播放该动画的 GameObject 上,这就意味着我们在场景中看到物体在 Y 轴上有位移,例如向上或者向下移动,但是该物体的 Transform 中的 Position 信息不会发生改变,会跟动画播放之前的 Position 信息保持一致;
      • Based Upon 或者 Based Upon (at Start),这个貌似有点不一样哦,在选中 Bake Into Pose 之后会变成 Based Upon (at Start),不勾选的时候是 Based Upon,不过这个就能理解了。不烘焙的话,那么 Root Motion 中 Y 轴的变化就依赖于选择的『Original 或者 Root Node Position』的 Y 轴位移变化,如果选择烘焙的话,那么就以这个动画的起始帧的 Y 轴作为整个动画 Root Motion 的 Y 轴位移,在整个动画播放的过程中,Y 轴的位移都是恒定不变的;
      • Offset,垂直方向上的偏移;
    • Root Transform Position(XZ),根节点位移信息(水平面,XZ 轴)
      • Bake Into Pose,勾选后会将根节点每一帧在水平面(X 和 Z 轴)方向上的运动信息烘焙到动画的骨骼运动中,在整个动画播放的过程中,根节点在 X 和 Z 轴方向的所有位移信息不会通过 Root Motion 作用到播放该动画的 GameObject 上,这就意味着我们在场景中看到物体在水平面上移动,但是该物体的 Transform 中的 Position 信息不会发生改变,会跟动画播放之前的 Position 信息保持一致,假如动画中物体会向前移动 3 米,我们会看到物体在整个动画播放过程中确实在向前移动,播放到最后一帧时确实向前移动了 3 米,但是当这个动画播放完毕之后,切换到任何其他的动画时,物体会直接闪回这个动画播放前物体所在的位置,所以通常我们需要保留动作位移的动画都不会勾选这个选项。那这个选项有神马用捏?例如某些待机动画,我们其实希望物体只是做一个待机动作,但是实际上不想让物体在水平方向上有位移,这个时候就可以勾选这个选项了,到时候看起来物体就像是钉在水平面上了;
    • Mask,这个掩码主要是用于控制动画播放过程中,各个骨骼之间的运动变换的
      • Definition,可以选择从动画文件创建也可以选择使用其他动画文件中已经创建好的配置;
      • Transform,这个就是动画文件中所有骨骼的层级关系,可以选择勾选那些需要应用动画中运动变换的骨骼;
    • Curves,这个主要用于设置某些跟动画相关的参数用,例如控制整个动画播放过程中的速度参数之类的,在动画播放的过程中可以通过 Animator.GetFloat(ParamName) 函数来读取曲线的值,曲线的 X 轴为动画的时间轴,Y 轴为曲线的值,曲线可以通过曲线编辑器进行增加关键点,调整曲线斜率进行编辑,读取时默认会根据当前动画播放的进度作为 X 轴的值进行读取,一个动画片段可以有多个曲线;
    • Events,这个是用于在动画播放的过程中触发事件的,例如整个动画中有起跳和落地两个事件需要在准确的时间点触发并通知到游戏中其他的对象,那么就可以在 Events 时间轴上新增事件通知,设置好触发的方法名称和参数,在播放该动画的 GameObject 上确保有某个脚本中有与该事件通知的方法签名一致的方法就好了,当动画播放到触发通知时间时,就会向 GameObject 广播该时间通知,脚本中方法签名一致的方法就会被回调了,那我们就可以做我们需要做的事情了。

说了这么多貌似跟 Root Motion 不是很相关的东西,那么究竟我们今天的主题是啥呢?肯定还是 Root Motion 这货。主要因为动画导入时的设置对于 Root Motion 的应用影响非常直接,所以前面絮絮叨叨地把这个动画导入设置都罗列了一遍。

回到正题,Generic 动画应用 Root Motion 有以下几个特点:

  1. Root Motion 仅仅作用于 GameObject 在 X 和 Z 轴上的位移变换,不影响 Y 轴上的位移。例如现在播放一个从地上向前空翻之后落地的动画,设置 Animator 的 applyRootMotion 为 True,也就是应用 Root Motion,那么动画在播放过程中,物体会在水平方向和垂直方向上都按照实际动画的运动轨迹进行运动,如果将 applyRootMotion 设置为 False,那么我们就只能看到动画在原地起跳然后再落地,动画中原本应有的在水平方向的位移就没有了;
  2. Root Motion 与导入动画时设置 Root Transform Position(XZ) 是直接相关的,如果我们选择了将 X 和 Z 轴方向上根节点的位移烘焙到动画骨骼运动中的话,那么动画播放过程中不论我们是否将 Animator 的 applyRootMotion 设置为 True 还是 False,动画播放过程中物体在 X 和 Z 上的移动是一定的,因为这个已经被烘焙到骨骼动画中,只要动画播放,物体就会移动,但是在动画播放的过程中 GameObject 的 Position 值不会改变,在动画结束后我们切换到其他动画的时候,其他动画开始播放时的 GameObject 的位置会回到这个动画播放前的位置,所以如果我们需要对某个动画应用 Root Motion 的话,那么这个动画在导入的时候就不要烘焙其在 X 和 Z 轴方向上的 Root Transform Position,让 Unity 自行根据动画中根节点的位移进行位移计算 GameObject 的位置信息;
  3. 注意 Root Motion 与 Rigidbody.Velocity 属性的关系,如果有两个动画 A 和 B,播放 A 动画的时候,希望 A 动画应用 Root Motion,而在播放 B 动画的时候不想应用 Root Motion,那么就直接在切换到动画 B 的时候,将 Animator 的 applyRootMotion 设置为 False 就 OK 了。但是如果播放动画的 GameObject 带有 Rigidbody 组件,那么需要注意一点,在播放 A 动画时 Rigidbody 的 Velocity 并不会在切换到 B 动画时清零,也就是说如果 A 动画的运动速度较快,那么切换到 B 动画的时候,如果希望 B 动画播放的时候 GameObject 按照自己的设定轨迹运动,就需要自行手动在切换到 B 动画之前将 Rigidbody 的 Velocity 属性清零,防止 GameObject 按照 A 动画的运动惯性继续运动。这个问题在没有 Rigidbody 组件的 GameObject 上不会存在;

这边再岔开一下,说说这个动画跟 Rigidbody 之间的关系:

  1. 如果我们没有将 Root Transform Position 的 Y 和 XZ 轴进行烘焙的话,那么在动画播放的过程中,Rigidbody 将会自动获得动画中物体运动的速度信息,直接通过 Rigidbody.Velocity 属性就可以获得;
  2. 如果我们将 Y 轴进行烘焙,那么 Rigidbody.Velocity 在 Y 轴上的值将会一直为 0,对于 XZ 轴也是一样的,如果烘焙了 XZ 轴的位移,那么整个动画播放过程中,Rigidbody.Velocity 在 X 和 Z 轴上的值都会为 0;
  3. 如果播放动画的物体没有 Rigidbody 组件,那么动画的运动都会仅仅按照动画实际的位移来进行逐帧播放,不会出现上文中提到的动画播放切换之后还存在的运动惯性问题,因为物理引擎依赖于 Rigidbody 组件,如果没有该组件,所有动画的播放都只是逐帧播放动画,不会存在速度的概念只有移动位移。
  4. Rigidbody 使用使用重力对于动画在 Y 轴上的位移没有任何影响,不论是否对 Root Transform Position 的 Y 轴进行了烘焙。

转自:http://7dot9.com/?p=528

时间: 2024-09-25 07:27:21

Unity3D 中 Generic 动画导入设置和 Root Motion 之间的关系的相关文章

Git 工作区、版本库中的暂存区和版本库之间的关系

下图展示了 工作区.版本库中的暂存区和版本库之间的关系: 1. 理解 Git 暂存区 (stage) 在版本库 .git 目录下有一个 index 文件,下面针对这个文件做一个有趣的试验.要说明的是:这个试验用 1.7.3 版本的 Git 进行的,低版本的 Git 因为没有针对 git status 命令进行优化设计,需要运行 git diff 命令才能看到 index 文件的日期戳变化,具体操作步骤如下. (1) 首先执行 git checkout 命令(后面会介绍此命令),撤销工作区中 we

Linux中的文件描述符与打开文件之间的关系

1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录文件.链接文件和设备文件.文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符.程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误.如果此时去打开一个新的文件,它的文件描述符会是3.POSIX标准要求每次打开文件时(含socket)必须使用当前进程中最小可用的文件描述符号

每天进步一点点——Linux中的文件描述符与打开文件之间的关系

转载请说明出处:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件.目录文件.链接文件和设备文件.文件描述符(file descriptor)是内核为了高效管理已被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符.程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误.如果此时去打开一个

全网最通俗易懂理清mybatis中SqlSession、SqlSessionTemplate、SessionFactory和SqlSessionFactoryBean之间的关系

摘自:https://www.cnblogs.com/xiaoming0601/p/12166160.html 我潇洒的灰大狼又回来啦.今天送大家的一句话是: 保持耐心,永远年轻,永远热泪盈眶. 前言 先容我哭一会儿,呜呜呜~昨晚写了一半的文章,还没保存就盖上盖子准备回家,拔下电源准备把电脑塞进书包带回家完成时,懒惰阻止了我,最终还是没带回家,于是,遭报应了,今天早上来,电脑直接就是没电关机了,开机后写的文章再也找不回来了...(不争气的mac真是对不起我前面特地写了一篇文章来夸赞mac真香啊.

Android面试题 请解释下单线程模型中Message、Handler、MessageQueue、Looper之间的关系

简单的说,Handler获取当前线程中的looper对象,looper用来存放从MessageQueue中取出的Message,再由Handler进行Message分发和处理,按照先进先出执行. MessageQueue(消息队列):用来存放通过Handler发送的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列. Handler:是Message的主要处理者,负责Message的发送,Message内容的执行处理.例如将消息发送到消息队列(se

数据库中char、varchar、varchar2、nvarchar之间的关系

符串“abc",对于CHAR (20),表示你存储的字符将占20个字节(包括17个空字符),而同样的VARCHAR2 (20)则只占用3个字节的长度,20只是最大值,当你存储的字符小于20时,按实际长度存储. oracle中,会有一个varchar2型的数据类型,varchar2型与varchar型的区别: 1.varchar2把所有字符都占两字节处理(一般情况下),varchar只对汉字和全角等字符占两字节,数字,英文字符等都是一个字节:2.VARCHAR2把空串等同于null处理,而varc

单线程模型中Message、Handler、MessageQueue、Looper之间的关系

Handler简介: 一个Handler允许你发送和处理Message和Runable对象,这些对象和一个线程的MessageQueue相关联.每一个线程实例和一个单独的线程以及该线程的MessageQueue相关联.当你创建一个新的Handler时,它就和创建它的线程绑定在一起了.这里,线程我们也可以理解为线程的MessageQueue.从这一点上来看,Handler把Message和Runable对象传递给MessageQueue,而且在这些对象离开MessageQueue时,Handler

在Unity3D的Legacy动画系统中应用Root Motion

最近仔细比较了Unity3D目前版本中的两套动画系统:Legacy和Mecanim.Mecanim系统功能较之Legacy要强大很多,但是使用AnimatorController着实不方便(尽管使用AnimatorOverrideController可以避免重复编辑状态机),是因为游戏逻辑层面往往要用一个状态机或者类似的机制来控制角色的状态,而角色层面的状态逻辑和动画层面是无法一一对应的,两套复杂的状态机要配合起来...想想就觉得蛋疼啊!难怪很多朋友现在还在使用Legacy动画系统.Legacy

初识android中的动画

动画效果可以大大提高界面的交互效果,因此,动画在移动开发中的应用场景较为普遍.掌握基本的动画效果在成熟的软件开发中不可或缺.除此之外,用户对于动画的接受程度远高于文字和图片,利用动画效果可以加深用户对于产品的印象.因此本文给出安卓设计中几种常见的动画效果. 基础知识 在介绍安卓中的动画效果之前,有必要介绍一下安卓中的图片处理机制.图片的特效包括图形的缩放.镜面.倒影.旋转.平移等.图片的特效处理方式是将原图的图形矩阵乘以一个特效矩阵,形成一个新的图形矩阵来实现的.矩阵Matrix 类,维护了一个