图形引擎实战:Unity角色攀爬系统

本篇内容技术参考借鉴了Invector和Dias的角色控制方案,动画资源来自Maximo;

1.检测可攀爬物体

基本思路是先用射线检测去检测角色正前方是否有碰撞体,再进一步检测前方是否有障碍,碰撞体是否符合攀爬标准:

标记,可攀爬的物体可以单独一个Layer或者使用Tag标记方便管理;

表面角度,可攀爬的角度可以设置到45度以上;

附近无障碍,目标点周围至少人物宽度附近没有障碍且均可攀爬;

碰撞检测使用Physics.Linecast或者Physics.SphereCast都可以;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

2.进入攀爬状态

进入攀爬状态时播放进入动作,首先要对齐位置,将角色位置移动到合适和距离和朝向避免播放进入动作时穿模;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

在角色上以角色根节点为参照偏移存储一个位置,也可以直接挂一个空物体作为攀爬的参考点,这个点就作为需要对齐的位置;对齐过程可以使用协程来渐进式的对齐位置,避免位置跳变;

进入动作播放完毕后,记得关闭角色Rigidbody的重力,并开启Is Kinematic,避免刚体速度带来不必要的麻烦,后续攀爬过程中直接改变Tranform来实现位移,不再使用速度控制位移;

3.攀爬四方向移动

移动时需要实时的查询移动方向上是否可攀爬,此时的检测的方式为,先向角色的移动方向检测一小段距离是否有可攀爬碰撞(为应对内倒角型转角),若无碰撞再以此为起点,向墙面打射线检测;

由于墙面的不规则性和为应对外倒角型转角,在第二阶段检测时会检测不到侧面的墙体,可以在原本垂直墙面的方向的基础上朝移动方向的反方向偏移一定角度检测;

另外避免障碍使角色穿模,在角色移动前先向目标方向使用Physics.CapsuleCast在没有障碍碰撞时再移动;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

四方向的移动使用blendtree,在控制脚本中通过OnAnimatorMove函数应用 rootmotion:

通过 tranfrom.InverseTransformPoint(animator.rootPosition)可以获取当前动画的位移位置,只取该位移中的x,z分量加上当前的位置作为目标位置;然后通过lerp的方式修改当前position;

关于攀爬速度调整,因为移动是基于rootmotion的,所以直接通过设置动画的播放速度就可以直接调整攀爬速度且手和脚不会明显滑动;

如果想要角色仅在有输入时才有移动动作,无输入时定格在当前的姿势,可以将输入的大小(magnitude)作为blendtree的播放速度,当无输入时就会停止播放;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

4.攀爬四方向跳跃

检测跳跃的基本逻辑和检测是否可以移动类似,在输入方向上检测跳跃距离处是否可攀爬,另外需要检测跳跃路径上是否有阻挡,可以使用SphereCast或者在角色Y轴方向上分多段射线检测;

检测跳跃距离时,先从输入方向 * 跳跃距离处向角色前向检测是否有可攀爬位置,因为墙面不平整的原因每次跳跃都不会是固定距离,所以需要记录跳跃目标位置,在播放动画时将角色位置渐进移动到目标处,这一步可以在OnAnimatorMove中当角色处于跳跃状态时按当前动画的进度移动根位置;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

5.调整角色姿态

墙面凹凸不平时,希望角色能够贴合墙体表面的角度,通过raycastHit.normal表面法线信息获得墙面角度,这可以用来调整角色Y轴方向的旋转使其贴合墙面;

只用一个点的法线来调整旋转角度是不够的,还需要在X轴方向的旋转角度贴合。想要角色Y方向上整体贴合墙面,可以从角色腿部或者脚部向墙面检测一个点,取此点和攀爬点的连线的角度作为基准,调整角色Z轴方向旋转使其贴合;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

6.手部和脚部的Ik调整

IK部分的实现同样也可以通过手部和脚部向墙面检测碰撞位置,将一定范围内碰撞位置作为IKPosition赋值给Animator;

手部和脚部的位置可以通过

transform.InverseTransformPoint(animator.GetBoneTransform(HumanBodyBones.LeftHand).position)函数获取;

7.顶部攀爬退出/悬挂状态

到达墙壁的顶部后,同样使用射线检测顶部是否达到攀顶的条件(障碍、角度、地面等条件):

①.由角色胸部向上检测是否有障碍,可以移动;

②.由第一步检测结束位置向前检测是否有障碍;

③.由第二步结束位置向下检测地面,作为攀顶动作的参考点;

与进入攀爬时类似,在动画播放前对其位置(主要是角色离顶部的高度);

动图见链接:https://zhuanlan.zhihu.com/p/563121983

在顶部有直角型的阻挡时可以切换攀爬到悬挂状态,此时的检测方向则变为向上检测悬挂点,移动时叠加移动方向的偏移预先检测前方是否有可悬挂的点;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

8.不同高度的地形跨越:

针对不同高度的障碍,对应不同的动画跨越;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

对应最低高度的平台希望让角色直接走/跑上去,不需要特殊的动画,尝试过播放单独的抬脚动画反而不连贯;对于这种类似台阶高度的障碍,如果不做处理可能会由于刚体碰撞而卡住。这里我们先通过脚部向上一点的位置发射射线检测是否有台阶存在,进一步从一定高度向前延申然后向下射线检测获得台阶高度,然后将角色的移动速度映射到根节点与台阶边缘连线的方向上。这样角色将会获得斜向上的速度,顺利跨上台阶;

动图见链接:https://zhuanlan.zhihu.com/p/563121983

其他高度的地形障碍检测方式与前面的所提到的类似,先检测到障碍的存在,再从上方向下获得高度,并确认目标点周未空间是否允许跨越;接下来将角色位置调整到合适的位置,动画播放过程中使用matchtarget将手部或者脚部贴合障碍物的顶端。

关于角色动画切换:

1.直接使用动画状态机中的条件切换:

2.在控制脚本中, 调用Animator相关函数实现切换:

Animator.CrossFade 或者Animator.CrossFadeInFixedTime;

使用第一种方式就需要现在动画控制器界面连线并设置切换条件,并且在控制脚本中为animator赋值才能切换动画,第二种方式更加灵活,不用向animator赋值就可以无条件切换;

当然如果使用Animancer这种playable实现的动画播放器则只需要设置播放相关的动画片段即可。

动画位置匹配:

1.使用Animator.MatchTarget在动画的某个阶段将角色的某个位置匹配到响应位置;

2.在动画阶段直接修改角色的Transform将角色移动到合适的位置,一般会在动画播放开始前;

欢迎加入我们!

感兴趣的同学可以投递简历至:CYouEngine@cyou-inc.com

全部评论

相关推荐

不愿透露姓名的神秘牛友
昨天 10:48
点赞 评论 收藏
分享
斑驳不同:还为啥暴躁 假的不骂你骂谁啊
点赞 评论 收藏
分享
4 2 评论
分享
牛客网
牛客企业服务