在这个⾓⾊控制脚本中,可以看到很多新奇的写法。关键是,为什么要把移动量转化为转⾝量和前进量呢?要理解这个问题,⼀定要多进⾏游戏测试,体会转⾝与输⼊的关系。
当⾓⾊⾯朝前,输⼊朝后时,⾓⾊的前进量为0,转⾝量达到最⼤;⽽当⾓⾊⾯朝前,输⼊也朝前时,⾓⾊的转⾝量为0,前进量达到最⼤。
这种输⼊逻辑特别适⽤于拟真类⾓⾊控制的游戏,它可以让⾓⾊控制⾮常细腻和真实。但这种控制有个特点:不断向左⾛、向右⾛时,会发现⾓⾊在前后⽅向也有位移。拟真类的⾓⾊动作会造成运动不精确,其实这也符合真实世界的逻辑。
以上代码将世界坐标系的向量move,先转化到⾓⾊⾃⾝坐标系以便处理,转化之后move.z就是前进量了,但转⾝量是个难点。通过分析可知:当⾓⾊朝向与输⼊⽅向相反时,转⾝量达到最⼤;当⾓⾊朝向与输⼊⽅向相同时,转⾝量最⼩;当⾓⾊朝向与输⼊⽅向成90°时,转⾝量是⼀个中间值。⼤多数⼈不知道数学上哪种函数可以把⽐值转化为旋转,但通过阅读代码发现,Mathf.Atan2()函数适⽤于此情形。有兴趣的读者可以研究⼀下Atan2()函数的定义,它也属于三⾓函数中的反正切函数,但与常⻅的反正切函数定义不同。
总之,可以通过计算得到转⾝量m_TurnAmount和前进量m_ForwardAmount,⽽这⾥显然没有真正的移动代码,还需要继续分析。
以下是更新动画变量的UpdateAnimator()⽅法。
void UpdateAnimator(Vector3 move)
{
// 更新动画变量
// SetFloat的第3、4个参数是为了给输⼊加缓冲,减缓输⼊跳变,让动画
更顺滑
m _ Animator.SetFloat("Forward", m _ ForwardAmount, 0.1f,
Time.deltaTime);
m _ Animator.SetFloat("Turn", m _ TurnAmount, 0.1f,
Time.deltaTime);
m _ Animator.SetBool("Crouch", m _ Crouching);m _ Animator.SetBool("OnGround", m _ IsGrounded);
if (!m _ IsGrounded)
{
// ⼈物在空中时,动画的Jump参数由⾓⾊竖直速度决定
m _ Animator.SetFloat("Jump", m _
Rigidbody.velocity.y);
}
// 这是⼀段极为细致的代码,它的功能是确定跳起时哪条腿在前⾯
// 思路是根据当前动画播放时间,估算出当前是哪条腿在前。⽽且跑步速度
越快,跳起时腿就应该迈得越开
// jumpLeg参数是每⼀帧都要计算的,但只有在地⾯上时才会更新动画变量
// 这⾥思路有点绕,在地⾯上时更新jumpLeg变量并没什么效果,它是在空
中才起作⽤的
// ⼀旦起跳,动画变量jumpLeg的值是最近⼀次更新的值,到了空中就不会
再变了
float runCycle =
Mathf.Repeat(
m_Animator.GetCurrentAnimatorStateInfo(0).normalizedTime + m_
RunCycleLegOffset, 1);
float jumpLeg = (runCycle < k _ Half ? 1 : -1) * m _
ForwardAmount;
if (m _ IsGrounded)
{
m _ Animator.SetFloat("JumpLeg", jumpLeg);
}
// 脚本留了⼀个微调⾓⾊速度的属性m _ AnimSpeedMultiplier,通过
改变动画速度改变⼈物移动速度
// 能够这样做的前提是该⾓⾊⽤的是根⾻骼动画(Root Motion),动画决
定了运动
if (m _ IsGrounded && move.magnitude > 0)
{
m _ Animator.speed = m _ AnimSpeedMultiplier;
}
else
{
// 改变速度不能影响空中的动画
m _ Animator.speed = 1;
}
}
这⼀段代码最好理解的只有前6⾏(不算注释),恰好最有⽤的也只有前6⾏。也就是说,删除从runCycle开始的后半段代码并没有太⼤影响,但是从后⾯复杂的代码中可以学到和体会到很多东⻄。详细的代码分析已经写在注释中了,这⾥简单总结⼀下。第2段代码⽤了⼀种⾮常⿇烦的⽅式,计算了⾓⾊移动时哪条腿在前⾯,⽽哪条腿在前、两腿分开的⾓度决定了跳跃时的动作效果。
最后⼀段代码对动画速度的处理揭⽰了⼀个隐含的意思:实际上,⾓⾊的移动并不是通过修改transform组件控制的,也不是通过施加⼒、改变刚体速度控制的,⽽是通过动画直接控制的。这就引出了Root Motion(根⾻骼动画)的实际⽤法。
⼩知识
⾓⾊控制脚本的其他部分⾓⾊控制脚本是⽬前为⽌遇到的最复杂的脚本,共有230⾏左右且⽐较抽象。除Move⽅法和动画更新⽅法以外,最重要的⽅法就是CheckGroundStatus,它是通过向⾓⾊下⽅发射射线判断⾓⾊是否接触地⾯。
除了主要函数以外,该脚本有很多代码是为了处理细节⽽存在的,先不考虑细节(如下蹲逻辑)才能更容易把握住逻辑的脉络。
6.4.3 根⾻骼动画的运⽤
根⾻骼动画(Root Motion)在第6.1.4⼩节已经做了详细的介绍,本节将讲解如何在实际⼯程中运⽤它。
1. 启⽤根⾻骼动画的前提条件
根⾻骼动画是将⾓⾊的运动交给动画负责,以达到运动轨迹完美贴合动作的⽬的。Root(根)运动了,场景中的物体也就发⽣了运动。
美术设计师在制作⾓⾊动画时,让⾓⾊相对定位点发⽣位移和旋转。这些位移和旋转会记录在动画资源中,在播放动画时,引擎会将同样的位移和旋转应⽤到游戏物体上,从⽽让物体实际发⽣移动和旋转。
因此⾸先可以想到,要使⽤根⾻骼动画,动画资源中必须具有位移、旋转的信息。如果动画本⾝就不具有必要信息,那就不可能使⽤根⾻骼动画。其次,在导出动画资源时也要正确设置,这样才能让这些信息被引擎正确识别。根⾻骼动画的导⼊设置在3D模型和动画导⼊的界⾯中,位于Animation标签⻚的下⽅。以跑步动作资源HumanoidRun为例,选中其中的动画⽚段HumanoidRun,可以看到以Root开头的选项有3个,如图6-39所⽰。
①Root Transform Rotation,根的旋转。
②Root Transform Position (Y),根在y轴上的位移。
③Root Transform Position (XZ),根在⽔平⾯(xz平⾯)上的位移。
可以看出,要利⽤动画资源中的运动信息,还需要分别详细设置。如何设置与具体动画有关,这⾥给出重点的思路。每种动画对⾓⾊的位置、旋转影响不同,设置之前⾸先要思考动作是否改变实际的物体朝向和位置。例如,如果是向前跑步的动画,就需要改变⽔平⾯(xz)的位置,不改变y轴位置,旋转不重要(直线跑没有旋转);如果是原地转⾝动画,则要改变⽔平位置和旋转,y轴不改变。
其次,根据需要勾选Bake Into Pose(烘焙到⾻骼)选项。对重要的、确定需要改变的位置或旋转,取消勾选Bake Into Pose选项,对不改变的值反⽽要勾选。
最后,如果动作基于的参考点不同,效果也会有区别。因此还要设置每种运动的参考点,也就是Based Upon(基于的点),可以选择Center of Mass(质⼼)、Feet(脚)等选项,但需要根据动画⽽定,并根据实际测试效果调整。
在所有设置的下⽅,还有⼀个Motion→Root Motion Node,只有该选项设置为<None>的时候,才会出现前⽂提到的3个Root开头的选项。
建议读者借⽤跑步动画多尝试,改变其中的选项再运⾏游戏进⾏测试,这样会更清楚地理解每个选项的作⽤。例如,勾选RootTransform Position (XZ)的Bake Into Pose选项,在游戏中⾓⾊跑步会有奇特的效果。
⼩提⽰
对Bake Into Pose选项的解释
⼈物的运动细说起来有两部分:⼀⽅⾯是动画设计师关⼼的,
表现层⾯的⼈物运动;另⼀⽅⾯是技术⼈员关⼼的,⼈物逻辑上的运动。
以跑步动作为例,逻辑上跑步直接影响⽔平⽅向的位置,这是显然的。但对动画设计师来说,⼈物跑步时⾼低重⼼是有变化的,上下颠簸才⾃然。但这种上下的颠簸只是视觉上的,不能真的去影
响物体的位置,换句话说,跑步时逻辑上只做⽔平运动,不会上下运动。
这正是Bake Into Pose选项的作⽤。勾选了该选项,代表这个运动只是⾻骼运动,不属于根节点;⽽不勾选该选项,则代表它是真正的根节点的运动。只有根节点的运动才会变成物体实际的运动。
2. 动画状态机与根⾻骼动画
根⾻骼动画正确设置后就可以直接使⽤了。不过游戏的需求⽐较灵活,有时需要开启根⾻骼动画,有时需要关闭根⾻骼动画,⽽控制的开关位于动画状态机⾥。选中⾓⾊,观察它的动画状态机组件,可以看到Apply Root Motion(应⽤根⾻骼动画)选项,它代表是否启⽤根⾻骼动画。但是现在不能修改,提⽰信息是“Handled by Script(由脚本控制)”,其原因是在当前使⽤的脚本中有⼀个特殊的函数OnAnimatorMove()。当Unity发现脚本组件中有此函数时,就会在编辑器上隐藏Apply Root Motion选项后⽅的⽅框。
为了进⼀步学习,可以先将整个OnAnimatorMove()函数注释掉。等待编译后再观察动画状态机组件,会发现⽅框出现了。此时运⾏游戏进⾏测试,⼀边移动、跳跃,⼀边查看⽅框,会发现在跳跃时⽅框被取消勾选,⽽落地时⼜会被勾选,这是因为脚本中⼀直在修改这⼀属性,其修改如下。
// 启⽤根⾻骼动画
m _ Animator.applyRootMotion = true;
// 禁⽤根⾻骼动画
m _ Animator.applyRootMotion = false;
由于脚本⼀直在修改这⼀属性,因此在编辑器⾥取消勾选也没有效果。如果要试验没有根⾻骼动画的效果,可以将脚本中对applyRootMotion的赋值都改为false,这样⼀来⾓⾊就⼨步难⾏了,这也说明了这个⾓⾊的确是由根⾻骼动画控制运动的。总结来说,本⼩节讨论了运⽤根⾻骼动画的两个要素:第⼀,正确导⼊和设置动画;第⼆,在动画状态机中应⽤根⾻骼动画。
6.4.4 动画遮罩
在实际游戏开发中,经常有这种需求:玩家可以跑步,⽽跑步的同时可以持枪、持剑、持棍棒或是空着⼿,拿着不同武器时上肢的动作不⼀样。为了4种武器做4种跑步动画⾮常⿇烦,⽽且每多⼀种武器⼜多了⼀套动画,考虑到扩展性就更不现实了。
很容易想到,如果把腿部动作与上半⾝动作分离开,让⼿持各种武器的动作独⽴出来,叠加到跑步动作中,这样不就很容易扩展了吗?现代⾓⾊动画正是这样做的。这其中有⼀个关键技术,就是屏蔽动画的⼀部分——屏蔽跑步动作的上肢运动和持枪动作的腿部运动。
这种屏蔽⾓⾊的部分肢体运动的技术,就叫作动画遮罩,或称为肢体遮罩(Avatar Mask)。在Unity中,动画遮罩有两处可以运⽤:第⼀,每个动画在导⼊时都可以设置遮罩,此⽅法相当于只选⽤了动画的⼀部分;第⼆,创建新的遮罩⽂件Avatar Mask,遮罩⽂件可以⽤在动画状态机⾥。第⼀种⽅法更为彻底,第⼆种⽅法⽐较灵活,下⽂依次讲解。
在模型和动画导⼊设置⾥的Animation标签⻚下部,有⼀个Mask(遮罩)选项,打开它可以看到⼀个绿⾊的⾓⾊⽰意图,它可以⽤于设置⼈形⾓⾊的遮罩,如图6-40所⽰。这个图将模型分为头、双臂、⼿、躯⼲和腿6个部分,外加⼿和脚的“反向动⼒学(IK)”设置,再加上脚底椭圆形区域(会影响⾓⾊位置)。其中每⼀个区域都可以单击,使其在绿⾊与红⾊之间切换。其中绿⾊代表动画可以让这⼀肢体运动,红⾊则代表动画不会让这⼀肢体运动。
很容易试验出遮罩的效果。选择HumanoidRun动画,然后单击遮罩中的两个⼿臂,最后单击Apply(应⽤)按钮。可以在动画预览中看到⾓⾊跑动时不会摆动双臂,只会把双臂⼀直放在胸前,运⾏游戏后直线跑动也有同样的结果。
另⼀个⽤到动画遮罩的地⽅就是在动画状态机中的Layers(动画层)标签⻚⾥。在每⼀层右侧的设置栏,单击设置图标,可以看到⼀个Mask属性,如图6-41所⽰。需要给Mask属性⼀个“遮罩资源”,遮罩资源⽂件可以在Project窗⼝中直接创建(在Project窗⼝中单击⿏标右键,选择Create→Avatar Mask)。最后将遮罩⽂件赋给动画状态机⾥的Mask属性,就可以让这⼀层⾥所有的动画都被遮罩所控制。
文章来源:https://www.toymoban.com/news/detail-852460.html
选中创建的遮罩⽂件,可以看到⼀个熟悉的绿⾊⾓⾊肢体⽰意图,⽽且也可以任意编辑该⽰意图。例如,如果将遮罩的⼿臂变为红⾊,屏蔽⼿臂动画,且将该遮罩⽂件赋给动画状态机的Base Layer,那么当前动画状态机⾥所有的动画都会屏蔽双臂运动。
利⽤动画遮罩,为随意搭配和组合动画带来了可能性。叠加不同的动画可以借⽤动画融合树,很多时候还需⽤到动画层级。文章来源地址https://www.toymoban.com/news/detail-852460.html
到了这里,关于Unity 3D脚本编程与游戏开发【3.7】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!