6.2.3 动画的制作步骤
接下来为⾓⾊配上动画,整体思路分以下3个部分。⼀是准备好单个的动画素材,包含站⽴、跑和跳等动作。也就是说,把原始素材中的图⽚串联起来,形成⼏个单独的动画。⼆是⽤Animator把这些单独的动作有机地组合起来,形成⼀张状态转移图。
三是设置动画变量。⽤脚本控制动画变量,就能间接地控制StateMachine了。
1. 编辑动画
在Unity⾥可以直接编辑简单的动画。在菜单栏中单击Window→Animation→Animation,即可打开Animation窗⼝,并将它拖到合适的位置。⼀种值得推荐的窗⼝布局——Animation窗⼝和Game窗⼝在界⾯的下⽅,如图6-14所⽰。
注意,Animation窗⼝显⽰的⼀定是当前选中物体的动画,因此要选中Player物体后,再操作Animation窗⼝。
01 在选中Player物体的状态下,再在Animation窗⼝⾥单击中央的Create按钮,选择⼀个路径创建动画⽂件,如Resources/anims。这⾥先做站⽴动画,命名为Idle即可。
02 在Project窗⼝中找到图⽚素材Resources/sprites,然后再找到RobotBoyIdleSprite原始图⽚,单击⽂件左边的⼩三⾓,展开所有的Idle动作⼩图⽚。
03 选中所有的Idle动作图⽚,将它们拖⼊Animation窗⼝,会⾃动形成⼀个动画序列,如图6-15所⽰。
04 修改动画播放速率,改动原来的Samples(采样率、速率)参数,将默认的60改为24。
05 单击Animation窗⼝中的三⾓形播放按钮,就可以播放Idle动画,并在场景窗⼝中预览效果(在选中了Player物体时才能操作)。这样站⽴动画⽚段就准备好了。
06 添加跑步动画,单击Animation窗⼝左上⾓的动画名称,打开⼀个菜单,选择Create New Clip即可创建新的动画⽂件,如图6-16所⽰。此处还可以切换多个动画⽚段。
07与前⾯的操作完全相同,继续创建Walk(⾛路)、Run(跑步)、Jump(跳跃)、Crouch(下蹲)等动画,也可以暂时只创建⼀部分。
为了简化制作,Jump(跳跃)动画可以简化,只要表现出空中姿态即可。在编辑好的动画时间轴上,只保留第6帧,删除其他所有帧,并将第6帧左移到开头。这样跳跃动画实际上只有⼀帧,但⾜够表现空中的姿态。
经过以上操作,在Resources/anims⽂件夹下得到了⼀系列动画素材,如图6-17所⽰。
2. 编辑动画状态机
Animator也有⼀个独⽴的编辑窗⼝,单击菜单栏中的
Window→Animation→Animator就可以打开它,如图6-18所⽰。
Animator窗⼝与Animation窗⼝类似,也要选中物体再操作。
⾸先,选中场景中的Player物体,观察Animator的内容,会发现新建的⼏个状态已经⾃动添加到⾥⾯了。当然也可以随时删除状态(选中状态按键盘上的Delete键),或者把动画⽂件拖曳进Animator窗⼝创建新的状态。
在Animator窗⼝中,状态的位置可以随意拖曳;在任意状态上单击⿏标右键,选择Make Transition即可创建状态转移箭头,将箭头放在另⼀个状态上再单击⿏标左键,即可⽣成⼀个状态转移。
动画状态机中的Entry状态,代表⼊⼝,Entry所连接的第⼀个状态就是默认状态(⻩⾊标注);蓝⾊的Any State状态代表任意状态,巧妙利⽤它可以少连很多箭头,⽐较⽅便。进⾏⼀系列上述操作,最终形成⼀个状态转移图,如图6-19所⽰。
6.2.4 创建动画变量
初步创建好的状态转移图基本表达出了动画⽚段之间的转换关系,但还缺少具体⽽准确的逻辑定义。例如,在什么情况下,从站⽴状态转移到⾛路状态。
因此,需要创建⼀个“速度”变量,并明确设定:站⽴时,当速度⼤于0.01,则从站⽴切换到⾛路;⾛路时,当速度⼩于0.01,则从⾛路切换到站⽴;⾛路时,速度⼤于0.2则切换到跑步;跑步时,速度⼩于0.2则切换到⾛路。
单击Animator窗⼝左上⾓的Parameters(参数)标签⻚(⻅图6-20),可以看到⼀个空⽩的列表。单击右侧的⼩加号就可以添加新变量了。这⾥暂时只需要float类型的变量Speed和bool类型的变量Ground。注意变量类型和⼤⼩写,这在写脚本时会⽤到。
有了这两个变量后,再将逻辑条件设置到状态转移的箭头上。
01 单击从Idle到Walk的箭头,在Inspector窗⼝下⽅找到Conditions(转移条件),再单击加号,选择变量名Speed,判断条件为Greater(⼤于),值填写0.01,如图6-21所⽰。这样填写的意思就是,当Speed⼤于0.01时,从站⽴切换到⾛路。
02 根据上⽂所述,与Speed相关的箭头共有4个。从Walk到Idle,Speed⼩于0.01;从Walk到Run,Speed⼤于0.2;从Run到Walk,Speed⼩于0.2。
03 之后只剩两个箭头了,它们与Ground参数有关。从Any State到Jump,Ground为False(假);从Jump到Idle,Ground为True(真)。由于采⽤了Platform Character 2D脚本,就不需要再编写,因此可以进⾏简单测试,仅测试⾛、跑和跳跃动作。如果制作顺利,那么应该能通过左右移动看到⾛和跑的动作,按空格键可以看到跳跃动作。要注意勾选⾓⾊控制器的Air Control属性,允许⾓⾊空中移动,以便测试。
经过多次测试,会发现⾓⾊的动画状态转移⼗分缓慢,从站⽴到⾛、从跑步到跳跃,都有着明显的延迟。这些延迟是因动画过渡⽽产⽣的,下⼀⼩节将解释原因和修正⽅法。
⼩提⽰
⼀种动画调试⽅法
读者可能在测试时会发现很多问题,通常是操作失误造成的。这⾥介绍⼀种⾮常重要的调试⽅法。
在游戏运⾏的过程中,同时保持Animator窗⼝也可⻅。这时选中Player物体,也就是Animator所挂载的物体,可以看到Animator窗⼝会随着游戏运⾏显⽰当前动画运⾏的情况。包括当前是哪⼀个状态,播放到什么时间都有明确的表⽰,⾮常⽅便。
在实践中,由于写错了变量等原因,经常会出现动画卡在某⼀个状态的情况,⽤这种调试⽅法可以快速分析出是哪⼀步出现了问题。
6.2.5 设置动画过渡
选中Animator窗⼝中的任意⼀个状态转移箭头,都会在Inspector窗⼝中看到该转移箭头的具体属性,其中包含了动画过渡信息。最重要的参数有两个:Exit Time(退出时间)与Transition Duration(状态转换时间)。
Exit Time指的是在切换到其他动画之前,先要继续播放⼀段当前动画,然后再切换。Exit Time常⽤于⼀些不可直接中断的动画,例如,某些游戏的攻击收招、拔枪或收枪等动作,出于真实感或游戏机制的原因,必须完整播放完毕,才能切换到另⼀个动画。
Transition Duration指的是从状态A切换到状态B时,要经过⼀个“A+B”叠加状态的⼀段时间。这段时间⽤于让两个动作完美过渡,例如在动作真实的3D游戏中,玩家⾓⾊从⾛切换到跑、从站⽴到跳跃,都需要⼀⼩段⾃然转换动作的时间。
以上两个“时间”在属性窗⼝⾥都是⽤⼀个时序图表⽰的,从⽽可以看出两个动作之间的重叠部分,就是动画过渡的时间,如图6-22所⽰。
图6-22 动画过渡选项
回到实际中,现在做的2D动画不应该有退出时间,也不应该有状态转移时间。其原因有两点:第⼀,2D平台跳跃游戏的⾓⾊动作是很灵活的,⾛、跑和跳的状态本来就可以⽴即切换,不⽤特别强调真实性;第⼆,前⽂提到,帧动画是不⽀持动画融合的;即便加上过渡时间,也⽆法让动画系统⾃动插值计算过渡动作,最终玩家感受到的只不过是“延迟”⽽已。
因此,这⾥先取消Has Exit Time的勾选,然后再将TransitionDuration(s)设置为0,如图6-23所⽰。对所有的状态转移箭头都做同样的操作,让所有的过渡时间都变为0。
对⽐取消过渡时间前后的时间轴图,可以很清晰地看到动画状态被⽴即转换了。这个时间轴图的设计⼗分精巧,可以⽤⿏标直接拖曳⼩三⾓形或状态来改变参数,⾮常直观。再次运⾏游戏进⾏测试,发现之前严重的动画延迟问题完全解决了,动画的转换⼗分迅速。
6.2.6 ⽤脚本修改动画变量
由于采⽤了Platformer Character 2D脚本,因此只对关键的步骤做出解释。要⽤脚本修改动画变量,⾸先要获得Animator组件的引⽤。事先声明Animator类型的变量m_Anim,在Awake()⽅法中获取。
m _ Anim = GetComponent<Animator>();
然后在脚本中搜索关键字m_Anim,会发现很多对动画变量的操作,例如在FixedUpdate⾥有如下操作。
m _ Anim.SetBool("Ground", m _ Grounded);
// Set the vertical animation
m _ Anim.SetFloat("vSpeed", m _ Rigidbody2D.velocity.y);
这引出了Animator组件的常⽤脚本⽅法:设置动画变量。设置动画变量要⽤到⼀组函数,分别对应不同的变量类型:
void SetBool( string 变量名称, bool值);
void SetFloat( string 变量名称, float值);
void SetInt( string 变量名称, int值);
void SetTrigger( string 变量名称);
动画状态机⾥的变量也分布尔值、浮点数、整数和触发器4种类型,以上4个函数也分别对应这4种类型。操作对应的变量必须使⽤相应的⽅法,⽽且特别需要注意变量名称完全⼀致,英⽂的⼤⼩写也不能错。类型不同或名称不同,都会在游戏运⾏时提⽰错误,因此要多注意Console窗⼝的警告或错误信息。
这些⽅法都类似,只是变量类型不同。布尔值适合表⽰“是否落地”这样的信息,浮点数适合表⽰“速度”“转⾝速度”等连续变化的值,⽽整数适合表⽰“第⼏种武器”“攻击动作的第⼏段”这种整型数值。最后⼀个触发器⽐较特别,它与物理系统的“触发器”毫⽆关系,⽽是⽤于切换到会⾃动结束的动画。
例如,⼈物扣动扳机的动作。这个动作很短,⽽且每次触发都只播放⼀次,播放完成便⾃动结束,这类动作就很适合做成触发器控制。扔东⻄、开启闸门等⼀次性动作都很适合⽤触发器表⽰。⽽相对地,腾空的状态就要由脚本负责开启,也要由脚本负责关闭,⽽不能做成触发器。
6.2.7 脚本编程重点提⽰
动画系统真正的难点不在动画,⽽是⾓⾊控制器。⾓⾊控制器是Unity编程基础、3D数学和物理系统的综合运⽤。初学者⼀开始可以通过多阅读、多试验来学习,不必刻意追求从零开始就做⼀个完整的⾓⾊控制器。
结合实例中⽤到的两个脚本Platformer 2D User Control和PlatformerCharacter 2D来说,编程要点提⽰如下。
1. 负责⽤户输⼊的脚本Platformer 2D User Control
①在Update()⽅法中,每帧检测是否按下了跳跃键,保存在m_Jump变量中。
②在FixedUpdate()⽅法中,先获取左右输⼊,再获取下蹲按键,然后调⽤⾓⾊控制器的Move()函数。注意Move()函数也是在FixedUpdate()中调⽤的,属于物理更新。
有个难点,为什么跳跃要单独在Update()中获取?这是⼀个很细节的时序问题。简单来说,Update()和FixedUpdate()的运⾏时机不同。⽽且在FixedUpdate()中使⽤GetButtonDown()函数有时会失效(按下了按键,但物理更新时检测不到),因此代码中⽤m_Jump变量做了⼀次中转。
注意,关键是这个脚本⽤于处理玩家输⼊,且它调⽤了Move()函数。⾄于细节问题,仅仅是编写者采⽤了这种技巧,并不是⼀定要这么编写不可。检测按键按下的办法并不是只有这⼀种,可以借鉴这种⽅式,也可以改⽤其他⽅法。
2. 负责具体的⾓⾊控制的脚本Platformer Character 2D
①负责输⼊的脚本调⽤了Move()⽅法,就从Move()⽅法看起。
②参数float move是横向输⼊,它的取值范围为-1~1。
③忽略下蹲逻辑,move参数⽤于关键的两个步骤:控制动画和实际移动。
// ⽤move 参数作为动画的速度变量,控制站⽴、⾛、跑
m _ Anim.SetFloat("Speed", Mathf.Abs(move));
// 实际的⾓⾊移动只⽤了⼀句代码控制
m _ Rigidbody2D.velocity = new Vector2(move*m _ MaxSpeed, m _
Rigidbody2D.velocity.y);
④如果玩家朝向与输⼊⽅向相反,就调⽤Flip()⽅法转⾝。所谓的转⾝就是将x轴缩放改为-1,让图⽚镜像翻转。
⑤跳跃是重点,仅看Move()⽅法会看不明⽩,得先看FixedUpdate()⽅法。
private void FixedUpdate()
{
// 先将落地设为false,如果真落地了再设为true
m _ Grounded = false;
// 从⼦物体GroundCheck的位置开始,检测⼀个⼩圆范围
Collider2D[] colliders = Physics2D.OverlapCircleAll(m _
GroundCheck.position,
k _ GroundedRadius, m _ WhatIsGround);
// 如果这个⼩圆范围内有碰撞体(排除机器⼈⾃⾝),就代表真落地了
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i].gameObject != gameObject)
m _ Grounded = true;
}
// 设置动画变量Ground
m _ Anim.SetBool("Ground", m _ Grounded);
// 设置动画变量vSpeed(与起跳、落地动画有关,也可以简化)
m _ Anim.SetFloat("vSpeed", m _ Rigidbody2D.velocity.y);
}
⑥这⾥⽤到了第3章物理系统中的圆形射线检测,⽤⼀个半径为k_GroundedRadius的⼩圆判断Player物体的脚部是否与地⾯接触。⽽m_WhatIsGround就是LayerMask(层遮罩),⽤于指定哪些层是地⾯。
⑦结合整体逻辑,可以看出只有检测到是否落地,正确设置了m_Grounded变量,才能在Move()函数中决定是否能起跳,因为只有落地状态才能起跳。如果m_Grounded变量不正确,就会产⽣⽆限跳跃或⽆法起跳的bug。
以上这段代码虽少,但“五脏俱全”,不仅综合运⽤了物理射线检测、3D数学等知识,⽽且逻辑严密,⾮常具有学习价值。在此基础上可以做出⼆段跳、踩踏敌⼈和顶箱⼦等多种功能,这些都是对基础知识的综合应⽤。
文章来源:https://www.toymoban.com/news/detail-859143.html
文章来源地址https://www.toymoban.com/news/detail-859143.html
到了这里,关于Unity 3D脚本编程与游戏开发(3.4)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!