本文重点内容: 1、让墙边变为可攀爬并检测它们 2、贴在墙上,哪怕它们正在移动 3、攀爬中使用相对于墙的控制方法 4、沿着角落攀爬以及悬垂 5、站在斜坡的时候阻止倒溜
这是关于控制角色移动的系列教程的第8部分。它增加了对垂直表面攀爬的支持。
本教程是CatLikeCoding系列的一部分,原文地址见文章底部。
本教程使用Unity 2019.2.21f1编写。它还使用了ProBuilder包。
(有的时候你根本不想接触地面)
1 可攀爬的表面
除了行走和跑步之外,攀爬也是一个不错的选择。一般来说攀爬的自由度受限于梯子的角度和摆,但由于我们的运动是基于物理的,所有我们将支持攀爬所有设定为可攀爬的表面。所以第一步是检测我们何时接触到这些表面。
1.1 最大攀爬角度
在攀爬过程中,表面的最重要属性是其方向。如果一个表面算作地面,那么我们就可以在其上行走,因此它不算是可攀爬的。陡峭的表面可以攀爬,但这只能使我们爬到完全垂直的墙壁上。角度超出的话,就只能进行悬垂,这虽然困难,但仍然可以攀爬到一定程度。在最极端的情况下,我们最终会悬挂在天花板上。让我们通过可配置的最大攀爬角度(从90°到170°,默认值为140°,仅超出45°悬垂一点点)限制MovingSphere的爬升能力。我们不允许攀爬天花板,因为这时候用悬垂更符合真实情况。
(最大攀爬角度)
像其他最小点积一样,预先计算最小climb点积。
如果我们确实想像蜘蛛一样爬上天花板怎么办? 像蜘蛛一样攀爬更像是不顾及方向的到处走。最好的模型是利用局部重力运动,将其拉到接触面。本教程着重表达不同于普通行走的攀爬情形。
1.2 检测可攀爬表面
我们将检测可攀爬的表面,就像我们识别陡峭表面的方法一样,但我们会记录单独的攀爬接触数和法线,它们必须像其他方法一样在清除状态下重置。
然后在EvaluateCollision中,如果一个接触点不算作地面,则分别检查陡峭接触和攀爬接触。始终使用攀爬触点连接的物体,球体就有可能正在攀爬运动中的表面。
现在,假设我们会自动攀爬。要检查这一点,请添加一个Climbing getter属性,如果有任何攀爬接触,该属性将返回true。
1.3 不可攀爬的表面
能攀爬任何东西并不总是件好事。我们可以通过使用layer mask来限制可攀爬的内容。为可攀爬的物体添加一个专用层,也可以为不可攀登的物体添加一个专用层。因为我更喜欢默认情况下所有内容都是可攀爬的,所以我选择了后者,并添加了一个不可攀爬层。
(添加不可攀爬层)
添加攀爬mask配置选项。配置它等于Probe Mask,然后通过编辑他们的预置,为所有球体添加Unclimbable 层到Probe Mask。注意,你还需要在轨道相机的障碍物遮罩中添加新layer,否则它会忽略它们。
(climb Mask)
我们现在需要在EvaluateCollision中检查碰撞层两次,因此将其存储在一个变量中。
然后,仅在未被屏蔽的情况下包括攀爬接触。
1.4 攀爬材质
行走和攀爬是非常不同的身体运动。例如,如果我们的avatar有一个人形,那么每个运动模式都会有不同的动画,这就清楚地说明了哪个模式在使用。为了使简单球体的模式在视觉上不同,我们将使用不同的材质。为普通材质和攀爬材质添加配置字段。我用当前的黑色材质作为普通材质,用红色替代攀爬材质。
(球体材质)
在Awake中获取球体的MeshRenderer组件的引用,并将其存储在字段中。
然后在Update结束时为其分配适当的材质。
从现在开始,只要它碰到可攀爬的表面,球体就会变成红色。
(橘色的表面是不可攀爬表面)
2 沿着墙壁移动
现在,我们知道当我们与可攀登的物体接触时,下一步就是切换到攀爬模式,这需要粘附在墙壁或其他类型的表面上,并相对于墙壁而不是地面移动。
2.1 粘附墙壁
我们首先添加一个CheckClimbing方法,该方法返回我们是否正在爬坡,如果是,则使地面接触计数和法线等于它们的爬坡等效值。
检查我们是否有地面接触时,首先在UpdateState中调用此方法,因此攀爬会否决其他所有条件。
为了防止跌落,只有在不爬升的情况下,才在FixedUpdate应用重力
(紧贴墙壁,做一些控制操作)
2.2 相对于墙的移动
只要碰到墙,重力就会被忽略,只要我们保持在一个平坦的区域,就会一直粘在墙上。但我们也经常会失去对球体的控制,就像我们在不改变摄像机方向的情况下改变重力一样。在这种情况下,我们不想改变摄像机的向上矢量,因为它必须始终与重力匹配,否则又会容易迷失方向。所以我们要做的是相对于墙和重力做运动,而忽略相机的方向。
在调整速度中,首先检查我们是否在爬升。如果是,在投影到接触平面之前,不要使用默认的右轴和正向输入轴。相反,用上轴表示Z,用接触法线和上轴的交叉积表示x,这样就可以在接触墙壁时控制方向的切换。
(沿着墙壁运动)
这在直视墙壁的时候效果很好,但是在其他角度观察墙壁的时候就不那么直观了,因为控制的方向不能很好地对齐。例如,当按右直走到墙边时,在视觉上右会变成向后,向前会变成向上。
(只呈现向右的方向)
最极端的情况是当视线离开墙壁时,左边和右边的控件可能出现翻转。但从这个角度来看,这是一个尴尬的开始。为了让玩家在准备攀爬时换一个更好的视角,有一种选择是,把相机编程为可以自动完成转换,但这在角度任意的情况下是很难做到的,并且常常会导致玩家受挫。高级相机自动化不是本教程的一部分。
当我们移至不可攀爬的地面时,为什么会立即跌落? 因为我们使用物理来进行运动,球体会按照你定义的方向运动。如果遇到不可攀爬的表面,它会导致攀爬失败。所以一旦你从一个普通的表面爬到一个不可攀爬的表面,球体就会掉下来。在可攀爬的表面上则取决于玩家,所以可攀爬和不可攀爬的区域在视觉上是不同的。
2.3 攀爬速度和加速度
攀爬通常比跑步慢得多,并且还需要更精确的控制,因为轻微的停止就可能导致跌落,无论是在现实生活中还是对我们的地球而言都是。另外,减速会使控制方向切换更易于管理。因此,添加最大爬升速度和最大爬升加速度配置选项。我们希望低速和高加速度来实现最大控制,所以让我们使用2和20作为默认值。通常,你希望将速度保持在较低水平,但我将使用默认值的两倍进行快速测试。
(最大攀爬速度为4 加速度为40)
合适的最大速度可能会因不同的物理步长而有所不同,而物理步长与更新循环并不同步,因此我们再也无法在Update中确定所需的速度。因此,摆脱desiredVelocity字段,而是将playerInput变量提升为一个字段。
然后在AdjustVelocity中选择适当的加速度和速度,并在需要时计算所需的速度分量。
(不同的攀爬速度和加速度)
2.4 角落攀爬
现在,我们已经可以在内壁拐角处爬升,其中可爬升的表面朝向球体弯曲。但是目前任何角度的外角都无法攀登,因为经过它们会导致球体与墙失去接触并掉落。我们可以通过始终使球体向其爬升的表面加速来解决该问题。这代表了攀岩者的抓地力,为此,我们将简单地使用最大攀岩加速度。攀爬时在FixedUpdate中执行此操作,而不要施加重力。
只要我们没有太快移动(或者如果是动画的话,墙壁也不会太快),就可以使我们与墙壁保持接触,但会导致我们陷入90°的内角。可以通过稍微降低抓地力(例如最大加速度的90%)来避免这种情况,这只会使我们减速,而不再使我们停在内角上。
(沿着角落攀爬)
虽然这样做,抓地力加速减慢墙体的跳离。但为了跳起来之后突然离开地面。可以通过让攀登属性也检查自上次跳跃后是否有超过2个步长的动作来判定。
请注意,需要相对于最大爬升速度的高最大爬升加速度才能可靠地附着在表面上。除此之外,速度不能太高,否则球体可能会在单个物理步长中最终以太远的距离将其自身发射到离墙太远的地方。
2.5 可选的攀爬
现在,攀爬已经可以正常运作了。让我们使其成为可选项。我们通过“Climb”按钮进行控制,你可以通过以下步骤进行配置:进入Input项目设置,通过其上下文菜单复制Jump条目,将其重命名为Climb,然后将其分配给其他按钮。
只要按住按钮,我们就尽可能攀爬,因此我们通过Input.GetButton而不是Update中的Input.GetButtonDown进行检查。
现在,如果需要攀爬,我们仅应在EvaluateCollision中检查可攀爬的表面。
2.6 攀爬前先减慢移动
我们可以做的另一件事是,当我们还在地面上想要爬的时候,我们可以放慢动作。如果我们接近一堵墙,这就像是放慢速度,以期待一次攀爬。如果我们到达墙的顶部,它也会阻止我们突然逃走,从而改善控制。它还能有效地让攀爬按钮兼具慢速移动按钮的双重功能,如果你是用按键而不是控制杆来控制球体的话,这就很方便了。
我们可以通过在AdjustVelocity中使用最大爬升速度来完成所有操作,即使我们没有爬升,但我们在地面上并希望爬升。
但是,这还不足以防止球体在到达墙顶后可能进行的自我发射出去。为此,如果我们不是在攀爬,而是在地面上却希望攀爬的时候,我们还必须在FixedUpdate中将攀爬抓地加速度与重力一起应用。
(爬升和爬落)
现在,我们可以可靠地从墙的顶部移动到墙壁的一侧,我们也可以可靠地进入一种情况,在这种情况下,我们正在向前移动以开始向下爬升,然后又切换为再次向上爬升。只要我们不断向前推进,就可以反复进行。这是我们的控制切换方法的缺点。最好的攀爬方法是将相机朝向墙壁。
(方向反转)
2.7 在斜坡上站立
我们可以使用相同的技巧,使我们在地面上站立时仍能保持攀爬的抓地力。通常,重力应该将球体拉下,以便球体缓慢滑下斜坡,但是当静止不动时,自动施加力以抵消重力是有意义的。我们可以通过在地面上并且速度非常低(例如小于0.1,或者平方的情况下为0.01)时将重力投影到接触法线上来进行模拟。这样就消除了引起滑动的重力分量,同时仍将球体拉到表面。
2.8 爬出裂缝
不幸的是,当球体卡在缝隙中时,我们的攀爬方法不起作用,这是因为陡峭的接触点会转换为地面接触点。在这种情况下,我们最终会停留在有效的水平面上,这与我们的攀爬控制装置(主要是垂直表面)不起作用。为了摆脱这种情况,我们将跟踪我们检测到的上一次攀爬法线。
除了进行累积之外,每次我们在EvaluateCollision中获得一次正常的攀爬时都进行设置。
然后让CheckClimbing确定是否有多个攀爬接触。如果是这样,请对爬升法线进行归一化处理,然后检查结果是否算作地面,这可以让我们知道是否处在裂缝状态。要摆脱困境,只需使用最后的攀爬法线而不是合计值即可。这样,我们最终会爬上一堵墙,而不会卡住。
下一章节,介绍游泳。