第一人称角色控制器
最近在折腾Unity的第一人称角色控制器,所谓第一人称角色控制器就是指像FPS游戏那样使用第一人称视角,锁定鼠标控制头的转动观察周围
推荐使用CharacterController而不是Rigidbody,虽然刚体组件创建起来更简单,但是CharacterController拥有更多实用的功能,比如处理步距落差,斜坡行走,防卡墙里等等优点。
开始
我们准备两个组件,一个负责眼睛的转动(视野的旋转)、一个负责身体的物理运算(移动、跳跃等)
首先是控制画面旋转的脚本 MouseLook.cs
using UnityEngine;
public class MouseLook : MonoBehaviour
{
public float mouseSensitivity = 1f;
// Update is called once per frame
void Update()
{
RotateView();
}
private void RotateView()
{
// 首先获取鼠标的deltaX 和 deltaY,表示相对于上一帧移动了多少
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime * 10;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime * 10;
Transform character = transform; // 身体的Transform组件
Transform camera = Camera.main.transform; // 眼睛的Transform组件
// 将鼠标deltaX和deltaY转换成对应的旋转值并对各个物体进行旋转
// (注:两个Quaternion相乘表示旋转角度相加)
// 这里需要注意当进行上下旋转(pitch)时,由相机(眼睛)来承担旋转任务
// 左右旋转(yaw)时由身体来承担旋转,这和FPS游戏中是一样的
// y轴旋转量由character承担,x轴旋转量由camera承担
character.localRotation *= Quaternion.Euler(0f, mouseX, 0f);
camera.localRotation *= Quaternion.Euler(-mouseY, 0f, 0f);
// 最后限制一下pitch旋转角,避免超过-90和+90,如果超过了会出现下图的情形
camera.localRotation = ClampRotationAroundXAxis(camera.localRotation);
}
// 这里是参考了Unity官方的一个例子,这里他使用四元数运算把x轴角度限制在-90, 90之间
// 避免过度旋转导致可以看到身后的东西,具体的原理不做讲解,只需要知道方法的功能即可
// Unity内部使用四元数表示旋转,四元数相比欧拉角有许多优点,但是运算起来也更复杂
Quaternion ClampRotationAroundXAxis(Quaternion q)
{
q.x /= q.w;
q.y /= q.w;
q.z /= q.w;
q.w = 1.0f;
float angleX = 2.0f * Mathf.Rad2Deg * Mathf.Atan (q.x);
angleX = Mathf.Clamp (angleX, -90, 90);
q.x = Mathf.Tan (0.5f * Mathf.Deg2Rad * angleX);
return q;
}
}
接着是控制物理运动的脚本Move.cs
using UnityEngine;
public class Move : MonoBehaviour
{
public float moveSpeed = 1f;
public float jumpHeight = 1;
public float gravityMultiplier = 1f;
private CharacterController characterController;
private bool jump = false;
private Vector3 velocity;
void Start()
{
characterController = GetComponent<CharacterController>();
}
void Update()
{
// 当检测到Space键按下时,设置jump为true
if (!jump && Input.GetKeyDown(KeyCode.Space))
{
jump = true;
}
}
void FixedUpdate()
{
// 获取前后左右的delta值,按住wasd触发
// GetAxis()会返回-1, 0 , 1表示按下了左,没有按任何键,右这三种情况
// 上下方向也是一致的 上键: 1, 不按键:0, 下键:-1
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
// 计算出移动的方向,再乘以速度
Vector3 move = transform.forward * z + transform.right * x;
velocity.x = move.x * moveSpeed;
velocity.z = move.z * moveSpeed;
// 模拟重力,如果物体不在地上,则会每帧加上一点重力加速度,使其下落速度不断变快
// 知道于地面接触
if (!characterController.isGrounded)
{
// 这里的Physics.gravity会返回(0, -9.81, 0)这个值
// 这里参考了重力公式
velocity += Physics.gravity / 2 * Time.deltaTime * Time.deltaTime * gravityMultiplier;
}else{
// 当在地面上时给一个很小的力保持与地面的紧密接触
velocity.y = -0.01f;
}
// 跳跃,当检测到Space键被按下时
if (jump)
{
jump = false;
// 给一个向上的力(只能给一帧),重力参考了跳跃公式
velocity.y = Mathf.Sqrt(jumpHeight * -2 * -Physics.gravity.magnitude);
}
// 最后将这个deltaVelocity交给characterController进行移动
characterController.Move(velocity);
}
}
重力
这里使用物体自由落体瞬时速度公式来模拟瞬时重力(写在FixedUpdate里对应到每一帧)
deltaY表示瞬时下落的速度,g取-9.81,t表示下落的持续时间
跳跃
这里使用物体竖直上抛位移速度公式变换而来
v表示跳跃到h高度所需要的速度,g取-9.81,最后加上角色的其它运动量即可