什么是模板动画?模板对于熟悉C++、C#、Java编程的开发者可能都不陌生,在Unity中也经常使用同一个函数带上<>来返回指定的对象,这些都是模板类或函数的使用。简单来说,模板类或函数定义了一套处理方法的过程,而不具体指定处理的对象,通过Animation Rigging的确可以向模板类一样,创建一套处理动作的过程,而不指定具体的对象。比如我在后面将介绍到的更换手枪弹匣的Rig动画,在动画过程中定义了手枪当前弹匣掉落,左手去拿取备用弹匣,左手拿着备用弹匣找到枪的位置,移动左手和弹匣到手枪柄下方,左手向上推,将左手拿着的备用弹匣推入手枪枪柄弹匣处,最后左手回到原先的位置处。整个动画过程中的备用弹匣位置、枪的位置、枪中安装弹匣的位置、动画开始时左手的位置在动画中都没有定义,这些位置全部都可以在Editor中进行定义和修改,不管是在静止、跑动中、还是瞄准状态,更换弹夹的动画都可以随时执行,左手和右手都可以自动协调去完成整个的动作,这就是Rig模板动画的优势。
上图是前面描述的更换手枪弹匣的Rig动画文件,没有复杂的内容,只通过对6个权重值进行调整,就完成了更换手枪弹匣的全部动画过程,非常简单,没有复杂的位置变换、旋转变换和Curves曲线。
之前看了很多Animation Rigging的介绍文章,从官方的视频到文档,到各大神的分享,但几乎都是照搬了官方的介绍和例子,只是对例子进行了进一步的介绍,相信很多人在看完后,非常兴奋,但真正想用的时候,却发现不知道该如何使用,尤其是各个约束应该在什么情况下使用,什么时候应该添加Multi-Parent Constraint,什么时候应该添加Multi-Position Constraint,后面,我将抛开官方教程,结合实际开发需求,详细介绍一下Multi-Parent Constraint、Multi-Position Constraint、Two Bone IK Constraint这三个约束的具体作用,以及如何使用这三个约束来自己创建实时动画。
这里我将默认阅读者已经了解过Animation Rigging,也看过类似的视频介绍和文档,因此在文章中没有对Animation Rigging的详细配置作介绍,只是针对于应该在什么样的场影下支使用Animation Rigging的各个约束,以及如何让Animation Rigging和状态机相结合。如果在阅读后有什么疑问,或发现了我的一些错误,欢迎留言指正。
作为一个独立游戏开发者,在开发游戏过程中,往往需要找寻各种动画包去满足开发的需求,Unity商店提供的基本动作包和各种人物模型实际上已经可以满足大部分的开发需求,并且基本上可以作到“0”代码就可以开发出一款游戏。但在开发过程中,往往不只是这些简单的动画,还需要一些独特的,能体现出游戏背景的动画来丰富游戏角色,使游戏各具活力,这时候,你如果没有美术功底,想自己去实现一个独特的动画,基本上是不太可能的,并且也很难达到你想要达到的目的,而Animation Rigging则能够使这一切都成为可能,通过约束条件的设置,通过简单的位置和旋转调整, Unity Avatar的Muscle系统将自动完成动画的整个过程,只要你能想到,一切皆有可能。
Animation Rigging是对Animator动画系统的进一步丰富,使原先的开放式API接口进一步以图形化的方式来呈现,使Unity的”0”代码开发的理念又前进了一步。没有错,Animation Rigging可以通代码来控制权重,但是”0”代码开发才应该是Animation Rigging追求的目标。Animation Rigging的自制动画并不是让大家去录制Muscle动画,而是让开发者在动画系统中去控制权重,封装权重动画层中对权重的调节,从而实现对动画过程的封装,但是具体的位置点和目标点,却可以由开发者在Editor中通过约束对象来动态控制,约束对象的位置将对动画产生细微影响,但并不会对之前封装的动画产生影响(这里不产生影响,指的是名称和位置必需相同,希望官方在后面也应该有类似Avatar的系统来映射权重对象)。
在网上的一些Animation Rigging的介绍文章中,可能是因为版本的问题,都有一些错误的地方(当然也不排除是我本身版本的问题),如果有错误的话,还希望大家指正,后面先说一下需要注意的地方。
我使用的Editor的版本是v2022.1.16f1c1和v2020.3.38f1c1,Animation Rigging的版本是v.1.1.1,在使用这二个版本时,RigLayer对象都必需放在和Hips同一层级或更低级,而不能放在父级,否则运行时会报错,或直接崩溃。在官方和各大神的视频介绍中全部都是放在了父级。
RigLayer对象下可以有多个约束对象,并没有数量的限制,分成不同的RigLayer对象作为父级,是为了方便统一进行权重的约束,如果在运行时不需要去禁止或调整RigLayer的权重,可以放在同一个RigLayer对象下。
后面我将详细介绍一下四个常用的约束条件Multi-Parent Constraint、Multi-Position Constraint、Two Bone IK Constraint、Multi-Aim Constraint的作用,以及在什么时候使用哪一个或多个约束。
1、Multi-Parent Constraint约束组件:
在大部分的Rigging的实现中,都需要多次用到Multi-Parent Constraint约束。该约束的作用是将源对象与约束对象之间创建一个类似于父子的关系,在权重值为1时,约束对象将完全“依附”在源对象下,二者保持Pivot(或Center)的对齐,这二者是完全对齐,没有偏移量。源对象的位置变化和旋转都将会100%的反映到约束对象。位置轴和旋转轴哪些要受到影响,可以在Settings面板中设置。
在什么时候要使用Multi-Parent Constraint约束呢?当需要让源对象保持在某一个固定位置时,就需要通过Multi-Parent Constraint进行约束。这句话要如何理解呢?我们举几个例子:需要摆出某一个姿势,需要刀附着在刀鞘里,需要枪背在背上,那么就需要创建一个约束对象,并添加Multi-Parent Constraint约束组件。在一组连贵的动作中,如果有多个拐点,那么这几个拐点都相当于一个姿势,都需要创建一个拐点对象,并添加Multi-Parent Constraint约束组件。约束对象可以依附于任意一个源对象,可以在RigLayer对象下创建一个空对象作为源对象,然后调整该空对象到需要的位置,再为这个空对象添加Multi-Parent Constraint约束组件,只要权重值生效,约束对象就会动态地移动到源对象的位置,并且固定在这里,直到权重值被重新调整时。当这个空对象被固定在该位置后,这个空对象的移动和旋转都将会导致约束对象的移动和旋转,这句话可以这样理解,当约束对象固定到源对象的位置的过程中,也将同时继承这个源对象在初始位置时的旋转和比例。
2、Multi-Position Constraint约束组件:
从上面的描述可以看出,Multi-Parent Constraint约束组件可以实现让源对象移动到指定的位置,并且让源对象旋转,但是它有一个问题,无法调整偏移,即无法让约束对象相对于源对象的Pivot(或Center)的位置产生偏移,这时另一个约束Multi-Position Constraint组件就起作用了。在上述的源对象中再添加Multi-Position Constraint约束组件,就可以对约束对象在相对于该组件所指定的源对象位置处的偏移进行调整,调整方式是调整在组件的Settings面板下的offset的三个值。
这里要注意一点,一旦添加了Multi-Position Constraint约束组件,则对象的Transform的position的值将不再起作用,因为Multi-Position Constraint约束组件的目的就是限制位置,因此,其位置值将完全由Multi-Position Constraint约束组件来接管。但是Transform的rotation的值仍然是有效的。
Multi-Parent Constraint约束组件虽然不是对Multi-Parent Constraint组件的约束对象进行位置偏移的调整,但是当二个组件的约束对象相同时,却可以对Multi-Parent Constraint组件的约束对象起到偏移的作用。因此,如果一个对象同时添加了Multi-Parent Constraint约束组件和Multi-Position Constraint约束组件,则该对象最主要的作用是让约束对象去继承该对象的Transform的旋转,而约束对象的位置则由Multi-Position Constraint约束组件来确定。
Multi-Position Constraint约束组件还有一个重要的作用,就是让约束对象和源对象之间保持相对位置的同步。这句话可以这样理解,Multi-Position Constraint约束组件如果不设置偏移量,则约束对象的初始位置不会产生任何变化。当源对象位置发生了变化时,约束对象将叠加同样的变化量,从而使相对位置同步。保持相对位置同步,可以有效地改善动画,使动画不会变得那么呆板。
通过Multi-Parent Constraint约束组件,可以使对象固定在某一个位置,但是这个固定会使动画对象变得僵硬,其它骨胳在Avatar Muscle肌肉系统的约束下,会使其它的骨胳一起移动,但是约束对象被固定,因此附着在该约束对象周围的骨胳因为受到约束组件的作用,变得只能有细微的移动变化,就象一个轴点一样,这个轴点被固定了,其它地方的移动变化,因这个轴点位置被固定,使这个轴点周围变得僵硬。Multi-Position Constraint约束组件的另一个重要作用就是改善这个轴点,让这个轴点也能跟随动画产生晃动,从而使这个轴点表现的更加自然。这个轴点的晃动的依据就是Multi-Position Constraint约束组件中的源对象的设置,你可以添加多个源对象,当这些源对象中的一个或多个的位置发生变化时,也会让约束对象根据权重值而产生相对的变化,从而使原先由Multi-Parent Constraint约束组件固定的约束对象也能根据动画的变化而产生变化,从而使动画变得生动、自然。
通过上面的描述,应该比较容易理解Multi-Position Constraint约束组件的作用了,如果希望Multi-Parent Constraint约束组件的约束对象表现的更加自然,或需要调整约束对象在固定点的偏移,就可以再添加Multi-Position Constraint约束组件,并设置一个或多个源对象,使约束对象参照源对象的变化而变化,从而使约束组件变得更加生动、自然。
根据以上的原则,也可以很容易地知道,这二个组件添加在同一个对象中时,其约束对象是相同的,而Multi-Parent Constraint约束组件的源对象通常是包含约束组件的对象自身,因为要通过调整源对象的位置,来改变约束对象的位置,其多个源对象的用法,在后面的文章中会有介绍,比如在将刀从刀鞘中抽出时,需要有一个向上拔出的动作,就可以使用多个源对象来控制,当然方法不是唯一的,大家有兴趣可以自己先想一下。而Multi-Position Constraint的源对象,则可以是骨胳中的某一个,或多个骨胳。比如,把匕首挂在右大腿外侧时,因为正常情况匕首应该随着身体的移动而产生晃动,如果不设置相对位置移动,匕首将固定在那个位置。要想让匕首随着右腿的移动而晃动,匕首套的源对象就可以设置为2个骨胳,一个是Spine2胸部骨胳,一个是RightUpLeg右大腿骨胳,可以通过调节源对象的权重,来调节晃动的影响。
后续文章将详细介绍Multi-Aim Constratin约束组件和Two Bone IK Constraint约束组件,实例讲解如何实现手枪放在手枪套中,右手握住手枪柄准备抽出手枪的动画、右手抽出手枪持在手中的动画、右手抬起准备瞄准,同时左手移动握住右手手腕,呈现瞄准姿势的动画;匕首放在刀鞘中,右手握住刀柄准备拔出的动画、右手向上抽出匕首,然后向前平移手持匕首的动画、将匕首平移动到刀鞘上方,然后向下插入刀鞘的收刀动画、匕首先向左肋下移动,然后斜着向右上方挥动的格档动画、右手旋转小刀的Idle动画,这些所有的动画,全部通过Rig权重动画层来实现,通过一个简单的float变量就可以自由将动画在任意动作上进行转换。如果当前处于持枪瞄准的姿势,会先放枪,将枪放回枪套,然后在将匕首抽出刀鞘,所有动作都不会突兀的跳转,而是自然转换,手指也会根据持有武器的不同,而产生不同的变化,而在挥刀格档中,身体也会自然偏转,不会产生仅仅是手臂移动的僵硬感觉。所有的这些动画都是仅通过控制RigLayer的Rig组件权重和约束组件的权重来实现的,没有涉及任何Avatar的Muscle系统,即没有使用任何具体的position和rotation的curves,状态机和动画文件创建完毕后,在编辑器中直接调整对应的约束组件的位置,就可以改变动画的位置,而不必再重新修改动画文件,而这些Rig动画文件都是可以复用的。