Unity 对角色上坡的研究

 之前研究了在2D视角下搭建3D场景。

https://blog.csdn.net/qq_59141650/article/details/131335713

在搭建完成后,还需要设置角色与其交互的逻辑。

Unity自带Character Controller组件(以下简称CC),可以简单实现基本的移动与爬坡。但目前来看CC仍存在不少问题,例如上坡时X轴不减速、Slope Limit对胶囊体不起作用、丢失精度导致isGrounded反复跳动等,因此我采用了射线的方案来解决这些问题。

↑CC上坡演示

 在现实的上坡中,除了总速度分给Y轴以外,还需要额外对抗重力势能。即是说,X轴其实是经历过两轮减速的,而CC默认是不进行减速,直接套用就会显得很违和。

因此可以加入射线检测,测算出下一个落点和当前点位的坡角,然后将X轴速度分解成两个分速度即可。

            float vx=1;//X轴移动速度,即每帧的位移
            float vy=0;
            float vz=0;
            float pi=3.14159265f;//圆周率,用于计算坡角
            RaycastHit hitInfo;
            int layerMask= 1 <<6;//射线检测的层级,和地形物件一致
            int maxDistance=10;//射线长度,根据需要设置即可
            float newY=y;//移动后新点位的y值
            float xAdd=0,yAdd=0;//重新计算后的位移
            float r=0;
            int slopeLimit=30;//无法走30°以上的坡
            v3=transform.position;
            v3[1]+=5;
            v3[0]+=vx;
            if(Physics.Raycast(v3,Vector3.down,out hitInfo,maxDistance,layerMask))
            {
                newY=hitInfo.point[1];
            }
            if (newY>y+0.001f)
            {
                r = (float)(Mathf.Atan2(newY-y, Mathf.Abs(vx))/(pi/180));
                if (r<slopeLimit)
                {
                    CalcDirection(vx, r, out yAdd, out xAdd);
                    x+=xAdd;
                    y+=Mathf.Abs(yAdd);
                }
            }
            else{
                x += vx;
            }
public void CalcDirection(float num,float r,out float x,out float z)
{
    float pi=3.14159265f;
    x = num*((float)Mathf.Sin(r * (pi/180)));
    z = num*((float)Mathf.Cos(r * (pi/180)));
}

↑优化后效果

可以看到合速度成功分解为了X速度和Y速度,而slopeLimit也能对胶囊体起作用了。做到这一步已经基本足够,但如果采用瞬时速度,违和感仍然会存在:

这时就需要考虑重力势能了,根据公式mgh=1/2mv2 可知: Δv=√(2gΔh),每帧发生位移后减去一定的速度

int gravity=10;
float xAdd=0,yAdd=0;
if (r<slopeLimit)
{
    CalcDirection(vx, r, out yAdd, out xAdd);
    x+=xAdd;
    y+=Mathf.Abs(yAdd);
    vx-=(Mathf.Sqrt(2*gravity*yadd));
}

若希望持续速度也受此影响,可以先减速再计算位移(用此代码会有一定的误差)↓

int gravity=10;
float xAdd=0,yAdd=0;
if (r<slopeLimit)
{
    CalcDirection(Mathf.Abs(vx), r, out yAdd, out xAdd);
    if (vx > 0)
    {
        vx-=(Mathf.Sqrt(2*gravity*yadd));
    }
    else if (vx < 0)
    {
        vx+=(Mathf.Sqrt(2*gravity*yadd));
    }
    CalcDirection(vx, r, out yAdd, out xAdd);
    x+=xAdd;
    y+=Mathf.Abs(yAdd);
}

 

猜你喜欢

转载自blog.csdn.net/qq_59141650/article/details/131341110