Unity笔记-29-ARPG游戏项目-10-完善攀爬
之前的文章已经说明了攀爬系统的基本思路,这篇文章主要完善一下攀爬在一些场景下的攀爬效果
对于以下这种凸起的地形过渡
之前的攀爬系统只能做到对于比较平滑的凸起地形的过渡,角度一般要小于45度,但是像这种90度的凸起,是无法过度的,并且会卡模型导致bug,因此这种情况要特殊处理
思路解析
正常来看,我们需要从墙面过渡到另外一个墙面 ,如果是弧形的话之前的攀爬系统就足够做到了,但是现在是直的,因此,首先我们需要先判断当前的情况,墙面过渡凸起的角度问题,角度小的时候,能够自动过渡,角度过大的时候无法自动过渡,这个阈值在哪里?这需要测试得到结果,暂定45度,当角度达到阈值,此时正常过渡无法过渡,需要代码进行特殊过渡,首先从角色本身向墙面射出一条射线,获得当前墙面的法线,在将墙面的法线旋转,使其的朝向朝着人物左方或者右方,之所以要用墙面的法线是因为墙面可能是倾斜的。旋转后的法线乘以微小距离加上人物当前位置获得新的点,此点需要稍微远一点,至少此点的前方已经不再是当前墙面,在将该点向着当前墙面方向移动微小距离,在从移动后的点向过渡的墙面射出射线,获得过渡后的点,直接赋予角色即可。
仅仅看文字不一定看的懂,这里我在画一个图方便解释
对于以下这种凹陷的地形过渡
这种的处理和上面差不多。同样的,当角度比较小的时候也能过自动过渡,超过阈值时无法过渡,例如当前的这种近乎90度的凹陷,这种只需要向右或者向左射出射线,判断角度是否达到阈值即可,如果超过,那么直接将射线获得的过渡墙面上的点直接赋予角色即可
代码实例
public void ClimbToOtherFace_hump()
{
RaycastHit hit_wall;
Vector3 origin = roleTransform.position + roleTransform.up * GameConst.PLAYER_BODYCENTER_OFFSET;
Physics.Raycast(new Ray(origin, roleTransform.forward), out hit_wall, 0.5f, environmentLayerMask);
Vector3 verNormal = Quaternion.AngleAxis(-90, roleTransform.up) * hit_wall.normal;//右
//Debug.Log(verNormal);
RaycastHit hit_new_l;
RaycastHit hit_new_r;
Vector3 newOrigin_l = origin - verNormal * climbToOtherLength;
Vector3 newOrigin_r = origin + verNormal * climbToOtherLength;
if(Physics.Raycast(new Ray(newOrigin_l,roleTransform.forward),out hit_new_l, 0.5f, environmentLayerMask)&& !Physics.Raycast(new Ray(origin, -verNormal), climbToOtherLength+0.05f, environmentLayerMask))
{
if (Vector3.Angle(hit_wall.normal, hit_new_l.normal) > 40)
{
roleTransform.position = hit_new_l.point + hit_new_l.normal * wallOffset;
return;
}
}
else
{
Vector3 newOrigin_other = newOrigin_l + roleTransform.forward * 0.5f;
RaycastHit newhit;
if (!Physics.Raycast(new Ray(newOrigin_other, verNormal), out newhit, 1, environmentLayerMask)) return;
roleTransform.position = newhit.point + newhit.normal * wallOffset;
roleTransform.rotation = Quaternion.LookRotation(-newhit.normal);
return;
}
if(Physics.Raycast(new Ray(newOrigin_r, roleTransform.forward), out hit_new_r, 0.5f, environmentLayerMask)&& !Physics.Raycast(new Ray(origin, verNormal), climbToOtherLength+0.05f, environmentLayerMask))
{
if (Vector3.Angle(hit_wall.normal, hit_new_r.normal) > 40)
{
roleTransform.position = hit_new_r.point + hit_new_r.normal * wallOffset;
return;
}
}
else
{
Vector3 newOrigin_other = newOrigin_r + roleTransform.forward * 0.5f;
RaycastHit newhit;
if (!Physics.Raycast(new Ray(newOrigin_other, -verNormal), out newhit, 1, environmentLayerMask)) return;
roleTransform.position = newhit.point + newhit.normal * wallOffset;
roleTransform.rotation = Quaternion.LookRotation(-newhit.normal);
return;
}
}
public void ClimbToOtherFace_sunk()
{
RaycastHit hit_wall;
Vector3 origin = roleTransform.position + roleTransform.up * GameConst.PLAYER_BODYCENTER_OFFSET;
Physics.Raycast(new Ray(origin, roleTransform.forward), out hit_wall, 0.5f, environmentLayerMask);
Vector3 verNormal = Quaternion.AngleAxis(-90, roleTransform.up) * hit_wall.normal;//右
RaycastHit hit_l;
RaycastHit hit_r;
if(Physics.Raycast(new Ray(origin, -verNormal), out hit_l, climbToOtherLength, environmentLayerMask))//左
{
Debug.Log(Vector3.Angle(hit_l.normal, hit_wall.normal));
if (Vector3.Angle(hit_l.normal, hit_wall.normal) > 45)
{
roleTransform.position = hit_l.point + hit_l.normal * wallOffset;
roleTransform.rotation = Quaternion.LookRotation(-hit_l.normal);
}
}
if (Physics.Raycast(new Ray(origin, verNormal), out hit_r, climbToOtherLength, environmentLayerMask))//右
{
Debug.Log(Vector3.Angle(hit_r.normal, hit_wall.normal));
if (Vector3.Angle(hit_r.normal, hit_wall.normal) > 45)
{
roleTransform.position = hit_r.point + hit_r.normal * wallOffset;
roleTransform.rotation = Quaternion.LookRotation(-hit_r.normal);
}
}
}
攀爬至墙顶的过渡
这里存在一种情况,就是两个倾斜的墙构成的一个角,这个角是凸起的,当凸起的角度过大时,角色无法过渡,又因为角度不够攀顶,因此角色无法攀顶,就是出现BUG。
修复的思路还是比较简单了,类似于上面的射线测试,通过墙面法线获得平行于墙面向上的向量,向上一定距离再向墙面方向射出射线获得新的墙面,判断角度即可
代码实例
public void ClimbUpToLand_3()
{
RaycastHit hit_wall;
Vector3 origin = roleTransform.position + roleTransform.up * GameConst.PLAYER_BODYCENTER_OFFSET;
Physics.Raycast(new Ray(origin, roleTransform.forward), out hit_wall, 0.5f, environmentLayerMask);
Vector3 upNormal = Quaternion.AngleAxis(90, roleTransform.right) * hit_wall.normal;
Vector3 newOrigin = origin + upNormal * ClimbUpLength;
RaycastHit hit_new;
if(Physics.Raycast(new Ray(newOrigin,roleTransform.forward),out hit_new, 0.5f, environmentLayerMask))
{
if (Vector3.Angle(hit_wall.normal, hit_new.normal) < 70)
{
if(Vector3.Angle(hit_wall.normal, hit_new.normal) > 45)
{
roleTransform.position = hit_new.point+ hit_new.normal * wallOffset;
roleTransform.rotation = Quaternion.LookRotation(-hit_new.normal);
}
return;
}
}
ani.CrossFade(GameConst.CLIMBTOUP_STATE, 0f);
climbLand = true;
canClimb = false;
//moveTest.climbUpDelayCounter = climbUpDelayTime;
//RecoverRotation();
roleTransform.position += roleTransform.up.normalized * climbUpOffset;
Invoke("ExitClimb", ani.GetCurrentAnimatorStateInfo(1).length - 0.2f);
}