???? 简介
人物移动功能的实现或许用到
Character Controller
组件或许用到Rigidbody
刚体组件,前者可以直接实现在某些高地不平的地面上移动,而后者想要在具有一定角度的坡面上进行移动则需要通过代码实现。本篇内容介绍如何通过Rigidbody
刚体组件实现人物爬坡功能;
???? 实现原理
实现人物爬坡功能前,通过获取用户Horizontal
和Vertical
轴输入形成了一个移动方向Move Direction
,该方向平行于地面,驱动Rigidbody
沿该方向移动,代码如下:
//根据输入获取方向
protected Vector3 GetInputDirection()
{
//前方
Vector3 forward = Vector3.ProjectOnPlane(mainCamera.forward, Vector3.up).normalized;
//右方
Vector3 right = Vector3.ProjectOnPlane(mainCamera.right, Vector3.up).normalized;
//输入值
input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
//返回值
return input.x * right + input.y * forward;
}
protected override void Update()
{
base.Update();
//计算当前的移动速度
speed = walkThreshold + ((sprintThreshold - walkThreshold) * sprintFactor);
//获取方向
Vector3 direction = GetInputDirection();
//驱动刚体
rb.velocity += direction * speed * Time.deltaTime;
//目标旋转值
Quaternion targetRot = Quaternion.Euler(new Vector3(0f, Vector3.SignedAngle(transform.forward, direction, Vector3.up), 0f)) * transform.rotation;
//插值方式进行旋转
transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, Time.deltaTime * rotateSpeed);
}
如下图所示,我们只需要在上述基础上,从脚底向下发射一条射线(红色线),射线检测碰撞的法线(黄色线)会与向量Vector3.Up
形成一个角度a,角度a与坡度b(绿色线与蓝色线夹角)相等,因此求得角度a的值便知坡面的坡度。
得知坡度后,只需要根据坡度调整Rigidbody
刚体的移动方向Move Direction
即可。
???? 实现代码
protected override void Update()
{
base.Update();
//计算当前的移动速度
speed = walkThreshold + ((sprintThreshold - walkThreshold) * sprintFactor);
//获取方向
Vector3 direction = GetInputDirection();
//判断是否在坡面上
if (IsOnSlope(out Vector3 hitNormal))
{
direction = Vector3.ProjectOnPlane(direction, hitNormal).normalized;
}
//驱动刚体
rb.velocity += direction * speed * Time.deltaTime;
//目标旋转值
Quaternion targetRot = Quaternion.Euler(new Vector3(0f, Vector3.SignedAngle(transform.forward, direction, Vector3.up), 0f)) * transform.rotation;
//插值方式进行旋转
transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, Time.deltaTime * rotateSpeed);
}
//是否在斜坡上
private bool IsOnSlope(out Vector3 hitNormal)
{
Ray ray = new Ray(transform.position + Vector3.up * .1f, Vector3.down);
if (Physics.Raycast(ray, out RaycastHit slopeHit, 1f))
{
hitNormal = slopeHit.normal;
float slopeAngle = Vector3.Angle(hitNormal, Vector3.up);
if (slopeAngle < maxSlopeAngle)
{
return true;
}
}
hitNormal = Vector3.zero;
return false;
}
maxSlopeAngle
用于限制人物可以爬坡的最大角度,当实际坡度大于该值时,人物不可以向上爬坡。