Unity下,在不规则斜坡上移动解决方案和推导过程

假设有一个上下的旋转楼梯,人物按照第三人称向前移动,爬上楼梯
那么这个楼梯就成为了一个斜坡

我们来套用 unity的常规移动方案 的话
1.Transform.Translate;
常规的移动方案,向前走,在斜坡的状态下,人物会嵌入地面,然后再由物理系统把人弹出地面碰撞,
就会出现人物上下抖动,摄像机如果是挂在人物节点下面 跟随移动,那估计得眼花了

2.Rigidbody.MovePosition
3.Rigidbody.AddForce
物理移动方案,是在不规则地形时比较好用的方案,但是实际使用的时候会发现问题。


如果是这样的一个旋转楼梯,那么这个楼梯的轴心部分的斜度近乎90度,但是外边的斜度却是很平缓。
那么就说明了一个问题,人物在向上走的时候,实际上由于刚体重力,人物越走会向外侧漂移。
因为旋转楼梯是无法做到每一个人物所站的位置都是水平的
Unity下,在不规则斜坡上移动解决方案和推导过程_第1张图片
Unity下,在不规则斜坡上移动解决方案和推导过程_第2张图片
Unity下,在不规则斜坡上移动解决方案和推导过程_第3张图片

那么就考虑还有没有其他方案了

然后就想到了 Unity的 NavMesh
用导航网格来做,会发现,可以解决这个问题。
因为NavMesh的API,可以设置目标点,人物在网格上寻路,同时也不需要使用刚体和碰撞。
那么只需要确定下一步移动的目标位置就可以了
NavMeshAgent有Move函数也可以实现 想TransLate一样的移动方式.

移动到这里算是解决了。


那么另一个问题就是 如果有跳跃和下落,该怎么办,因为NavMesh本身是需要烘焙一个导航网格才能进行移动。
所以NavMesh依赖于网格之上,是没法进行跳跃和掉落的, 比如 走到边界了,无法寻路,就走不出去了。
那怎么样才能掉落那。。?

然后就想到了一个方案,做2套地形。一套大一些,一套正常的。 这样的情况下,烘焙大的地形作为导航,小地形作为碰撞。
当在导航网格上走,移动到脚下没碰撞的地方,就把NavMeshAgent.enable=false,用刚体让人掉下去。
同理,跳的时候,关掉NavMeshAgent,落地再打开。

然后就遇到了问题,导航网格和地面 并不一定贴合,碰撞体的判断,其实不精准。
有时候可能看到人在导航网格上,但是没有碰到碰撞体。这就很尴尬了。
为了增加精准的判断,所以就需要做一个向下的射线,判断距离。


oh~~越想越复杂。

整理一下需求:
1.人需要在斜坡上走
2.有物理效果,可以跳,可以掉落

然后回顾一下之前的方案,NavMesh直接就否了,网格和碰撞贴合是不受我控制的(如果模型网格就是有强烈凹凸坑点的情况下)
物理方案也是不行的,因为存在旋转楼梯的问题。
那么就只剩下Transform的方案了

常规的认知下,人物是Transform.forward方向移动,就一定会像上面所说的,嵌入地板再弹出。
会造成抖动情况,是因为嵌入地板,那么解决嵌入地板的问题就好了,只要每次移动都是正确的向量,不嵌入地板,那其实就没问题了。

豁然开朗了,那么要做的事情就很清楚了。
其实只用获取到移动向量,其实就是坡度,就能走斜坡了,并且兼容常规的rigidbody,可以踩空掉落。

Vector3 v3CurPos = transform.position;          // 当前坐标点
Vector3 v3DirDis = transform.forward * m_nMovePower * Time.deltaTime;   // 假想目标点;
Vector3 v3Target = v3CurPos + v3DirDis;         // 这一帧,如果是平行向前,应该走到什么位置;

if (!m_isJump)
{
    // 射线部分
    Vector3 v3CastFrom = v3Target + m_v3LineCastOffset;
    Vector3 v3CastTo = v3Target - m_v3LineCastOffset;
    Debug.DrawLine(v3CastFrom, v3CastTo, Color.black);
    RaycastHit rh;
    if (Physics.Linecast(v3CastFrom, v3CastTo, out rh, 1 << LayerMask.NameToLayer("ground")))
    {
        Vector3 hitPos = rh.point;
        Vector3 dir = hitPos - v3CurPos;
        dir.Normalize();

        v3DirDis = dir * m_nMovePower * Time.deltaTime;
    }
}

// 实际移动;
Debug.DrawLine(v3CurPos, v3CurPos + transform.forward * 10, Color.red);
transform.Translate(v3DirDis, Space.World);

那么便有了这段逻辑,
m_isJump = 是不是在跳跃,因为起跳的时候继续射线判断,会认为在地面上
Unity下,在不规则斜坡上移动解决方案和推导过程_第4张图片
先取得当前帧应该走到的方向位置(红线1),取得地面投影的位置(绿线2),就是地表位置,那就能知道这次移动正确的向量(紫色3)

向量*当前帧距离=当前帧的目标位置。

完美解决走斜坡


程序学无止尽。
欢迎大家沟通,有啥不明确的,或者不对的,也可以和我私聊
我的QQ 334524067 神一般的狄狄

你可能感兴趣的:(Unity技术,C#技术,unity3d,游戏开发,算法)