场景
- 做一个简单的任务移动控制
- wasd的键盘移动
- 跳跃
- 地面检测
- 跳跃时保留地速
1.character controller
- 调整好角色控制器,通过角色控制器的API来进行操作角色移动
2.通过键盘操控前后左右移动
- 因为速度是通过键盘每帧的输入进行累加的,因此先创建一个速度向量
Vector3 motionVector = Vector3.zero;
float horizontal_axis = Input.GetAxis("Horizontal");
float vertical_axis = Input.GetAxis("Vertical");
- 对速度变量进行叠加
- 注意这里是对角色自身的方向来移动
- 注意下面这个wasd方向的位移结构都是:1.方向单位向量 2.速度 3.输入 4.(非必须)时间间隔,换成0.02也是可以的
- 注意一定是速度的累加,因为键盘输入的值是会变的,是慢慢增长的,松开是会回落的
motionVector += playerTransform.forward * (moveSpeed * vertical_axis * Time.fixedDeltaTime);
motionVector += playerTransform.right * (moveSpeed * horizontal_axis * Time.fixedDeltaTime);
characterController.Move(motionVector);
3.重力作用
- 根据物理公式,自由落体的速度公式是:Vt = gt,也就是加速度 * 时间,下面即使速度的大小
verticalVelocity -= gravity * Time.fixedDeltaTime;
- 然后将速度叠加到全局坐标系的垂直方向,这样就可以实现下落
motionVector += Vector3.up * (verticalVelocity * Time.fixedDeltaTime);
4.地面检测
- 因为如果不用地面检测将垂直方向的速度置为0,那么角色即使落地停止,但是它的垂直方向的速度还是会一直随着时间叠加。
- 因此需要进行地面检测,接触地面后,垂直方向的速度置为0
- 地面检测可以通过很多种方式(可以通过ray射线,也可以通过碰撞)
- 这里使用的方式是后者,注意在Player的character controller下方圆截面中心点,放置一个地面监测点
- 这样通过下面这个方法,会以这个中心点,以一个半径的距离来检测地面(isGround 是一个bool值)
isGround = Physics.CheckSphere(GroundCheckPoint.position, checkShereRadius, GroundLayer);
- 当然这里也是有点坑的(1.需要给需要检测的物体上一个LayerMask;2.需要考虑一下character的Skin Width皮肤厚度,因此半径应该等于character的Radius 加上 Skin Width)
5.跳跃
- 将世界坐标下的垂直方向的速度改成向上的一个初速度就可以了
- 那么这个速度是多少呢?需要通过最大跳跃高度来计算比较合适。
public float MaxJumpHeight = 5f;
- 因为垂直上抛运动的上升过程和下降过程是镜像对称的(高中知识),因此起跳速度应该是自由落体MaxJumpHeight 高度的下落速度的反向速度。
- 公式是 Vt = 根号下(2gh)
- 因此这样计算比较合理
if (isGround)
{
if (Input.GetButtonDown("Jump"))
{
verticalVelocity = Mathf.Sqrt(2 * gravity * MaxJumpHeight);
}
}
6.跳跃的时候保持地速
- 这样比较真实。。不然跳上箱子什么的,通过助跑也上不去。。回原地跳
- 声明两个用于记录起跳时瞬间的水平速度
private float tmp_horizontal_axis;
private float tmp_vertical_axis;
- 然后在起跳的瞬间,记录一下当时的键盘输入的float浮点值
if (isGround)
{
if (Input.GetButtonDown("Jump"))
{
tmp_horizontal_axis = horizontal_axis;
tmp_vertical_axis = vertical_axis;
verticalVelocity = Mathf.Sqrt(2 * gravity * MaxJumpHeight);
}
}
if (isGround)
{
motionVector += playerTransform.forward * (moveSpeed * vertical_axis * Time.fixedDeltaTime);
motionVector += playerTransform.right * (moveSpeed * horizontal_axis * Time.fixedDeltaTime);
}
else
{
motionVector += playerTransform.forward * (moveSpeed * tmp_vertical_axis * Time.fixedDeltaTime);
motionVector += playerTransform.right * (moveSpeed * tmp_horizontal_axis * Time.fixedDeltaTime);
}
- 再优化一下~在地面上的时候才接收键盘的输入,这样就比较完美了!
float horizontal_axis = 0f;
float vertical_axis = 0f;
if (isGround)
{
horizontal_axis = Input.GetAxis("Horizontal");
vertical_axis = Input.GetAxis("Vertical");
motionVector += playerTransform.forward * (moveSpeed * vertical_axis * Time.fixedDeltaTime);
motionVector += playerTransform.right * (moveSpeed * horizontal_axis * Time.fixedDeltaTime);
}
else
{
motionVector += playerTransform.forward * (moveSpeed * tmp_vertical_axis * Time.fixedDeltaTime);
motionVector += playerTransform.right * (moveSpeed * tmp_horizontal_axis * Time.fixedDeltaTime);
}
7.完整的代码如下
- 记录而已,有不好的地方请指正
- 下面是包含了键盘移动和鼠标控制视野的代码
public class PlayerMovementController : MonoBehaviour
{
[Space(20)] public float rotateSpeed = 180;
[Range(1, 2)] public float rotateRatio = 1;
public Transform playerTransform;
public Transform eyeViewTransform;
public float MaxViewAngle = 65f;
private float tmp_viweRotationOffset;
public float gravity = 9.8f;
public float verticalVelocity = 0;
public bool isGround = false;
public LayerMask GroundLayer;
public Transform GroundCheckPoint;
public float checkShereRadius;
public float MaxJumpHeight = 5f;
private float tmp_horizontal_axis;
private float tmp_vertical_axis;
[Space(20)] public CharacterController characterController;
public float moveSpeed = 10;
private void Start()
{
characterController = this.GetComponent<CharacterController>();
}
private void FixedUpdate()
{
PlayerRotateControl();
PlayerMoveControl();
}
public void PlayerMoveControl()
{
if (characterController == null)
{
return;
}
Vector3 motionVector = Vector3.zero;
float horizontal_axis = 0f;
float vertical_axis = 0f;
if (isGround)
{
horizontal_axis = Input.GetAxis("Horizontal");
vertical_axis = Input.GetAxis("Vertical");
motionVector += playerTransform.forward * (moveSpeed * vertical_axis * Time.fixedDeltaTime);
motionVector += playerTransform.right * (moveSpeed * horizontal_axis * Time.fixedDeltaTime);
}
else
{
motionVector += playerTransform.forward * (moveSpeed * tmp_vertical_axis * Time.fixedDeltaTime);
motionVector += playerTransform.right * (moveSpeed * tmp_horizontal_axis * Time.fixedDeltaTime);
}
verticalVelocity -= gravity * Time.fixedDeltaTime;
motionVector += Vector3.up * (verticalVelocity * Time.fixedDeltaTime);
isGround = Physics.CheckSphere(GroundCheckPoint.position, checkShereRadius, GroundLayer);
if (isGround && verticalVelocity < 0)
{
isGround = true;
verticalVelocity = 0;
}
if (isGround)
{
if (Input.GetButtonDown("Jump"))
{
tmp_horizontal_axis = horizontal_axis;
tmp_vertical_axis = vertical_axis;
verticalVelocity = Mathf.Sqrt(2 * gravity * MaxJumpHeight);
}
}
characterController.Move(motionVector);
}
private void PlayerRotateControl()
{
if (playerTransform == null || eyeViewTransform == null)
{
return;
}
float offset_x = Input.GetAxis("Mouse X");
float offset_y = Input.GetAxis("Mouse Y");
playerTransform.Rotate(Vector3.up * (offset_x * rotateSpeed * rotateRatio * Time.fixedDeltaTime));
tmp_viweRotationOffset -= offset_y * rotateSpeed * rotateRatio * Time.fixedDeltaTime;
tmp_viweRotationOffset = Mathf.Clamp(tmp_viweRotationOffset, -MaxViewAngle, MaxViewAngle);
Quaternion EyeLocalQuaternion = Quaternion.Euler(new Vector3(tmp_viweRotationOffset,
eyeViewTransform.localEulerAngles.y,
eyeViewTransform.localEulerAngles.z));
eyeViewTransform.localRotation = EyeLocalQuaternion;
}
}
|