之前研究了在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);
}