关于角色控制器的基本用法我就不做介绍了,请自行查看相关文档:
Unity - Manual: Character Controller component reference
本文用到了三角函数和插值函数,非常简单,如有疑问请查询以下文章:
首先来看实现效果
![](https://i-blog.csdnimg.cn/direct/2742fc00401f48d5914a935bc1741138.gif)
目录
[2. 旋转轴](#2. 旋转轴)
0.玩家与组件基础
基础场景
![](https://i-blog.csdnimg.cn/direct/e30b73eedd2948389543f51df1122f68.png)
结构关系
第一人称视角,主摄像机在玩家的头部做子对象没什么好说的
![](https://i-blog.csdnimg.cn/direct/6cf85b4307d24647b6a21a3bed167019.png)
实际图
![](https://i-blog.csdnimg.cn/direct/e3c39acc358a47bd930f5aa16387fa2f.png)
组件一览
这里要注意,Character Controller本身就是一个另类的碰撞器 ,所以不要再加capsule collider了,但可以添加刚体组件,因为碰撞器只提供碰撞/触发检测,不能模拟物理效果
最后一个脚本是我们本次要编写的
![](https://i-blog.csdnimg.cn/direct/e14562327d6545938bb54a6f01ab7f62.png)
辅助功能:
开发者/用户鼠标隐藏:
cs
private void HideMouse(){
// 锁定鼠标光标并隐藏
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
简单准心
![](https://i-blog.csdnimg.cn/direct/169ef8d8e08a4e0e9de71eb9aab4458f.png)
![](https://i-blog.csdnimg.cn/direct/eeda305696e4422d9a08111abac5e47b.png)
基本变量
cs
private Camera HeadCamera;
private CharacterController Controller;
1.玩家的视角钳制 ![](https://i-blog.csdnimg.cn/direct/2cfd4327e6a74fb2aeb995f4b94319ab.gif)
首先来看我们实现控制视角的时候都需要注意什么地方:
1.视野分析
人的头能抬起和看下的角度不同,还有眼睛的转动配合 会导致视野情况因人而异(大致在-90°到90°),因此我们直接不考虑眼球转动 直接让玩家的视野钳制在±90°即可
横向我们就不考虑了直接做360°旋转即可也就是±180 不做任何钳制
![](https://i-blog.csdnimg.cn/direct/d4d89b899d474e2387b1442488d6b624.png)
2. 旋转轴
![](https://i-blog.csdnimg.cn/direct/6460f19a53674a67a1b3e2efee19de9b.png)
注意加的方向,这里有一个点是比较容易忽略的**,我们角色向下看是对x值做加法**
![](https://i-blog.csdnimg.cn/direct/e3a90a07fbbf4d678d3c9c1e95bdc59f.gif)
但是我们的十字轴 向下拖动是负方向
所以在代码之中,要注意这一点
3.变量一览
cs
public Vector2 xY;
[Header("鼠标灵敏度")]
public float mouseSensitivity = 1.0f;
private Vector2 VirticalPersective = new Vector2(-90, 90);
private float currentVerticalAngle;
4.功能实现
cs
private void PersPactive() {
xY = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * mouseSensitivity;
currentVerticalAngle -= xY.y;
currentVerticalAngle = Mathf.Clamp(currentVerticalAngle, VirticalPersective.x, VirticalPersective.y);
//相机上下视角
HeadCamera.transform.localRotation = Quaternion.Euler(currentVerticalAngle, 0f, 0f);
//角色旋转
this.transform.Rotate(Vector3.up *xY.x);
}
需要注意的一点是,上下控制相机旋转,左右控制角色旋转,不要搞混
2.玩家的移动控制
这个其实就没有什么好讲的直接看代码
cs
private Vector2 xZ;
Vector3 moveDirection;
[Header("移动速度")]
public float moveSpeed = 3.0f;
[Header("跑步速度")]
public float runSpeed = 6.0f;
cs
private void Move(){
xZ = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
moveDirection = (transform.forward * xZ.y + transform.right * xZ.x).normalized * moveSpeed *Time.deltaTime;
if (Input.GetKey(KeyCode.LeftShift)) {
moveDirection = (transform.forward * xZ.y + transform.right * xZ.x).normalized * runSpeed * Time.deltaTime;
}
Controller.Move(moveDirection);
}
3.摄像头的摇晃
我们只需要分析一下相机应该怎么动
假设这个是相机:
![](https://i-blog.csdnimg.cn/direct/c43751381c6140759005610a8d652dfb.png)
想要其做简谐运动第一时间想到的肯定是三角函数
![](https://i-blog.csdnimg.cn/direct/d16125d780a541359252ae31aa3cb9aa.png)
三角函数必然要有一个连续的输入所以输入值 也就是三角函数的参数取一个Time.time作为横轴移动
![](https://i-blog.csdnimg.cn/direct/35e903b07aa8426ebc55d8cd0f3ffbec.png)
但是单纯的左右或上下移动摄像机只会很突兀就比如下面这样:
单纯x跟蛇一样
![](https://i-blog.csdnimg.cn/direct/770a1982913a4ba7ae7b4aa2878752a2.gif)
单纯y一跳一跳的
因此需要将二者结合 然后设计一个数字去调整其周期,这里我已经做好了 测试
相机的位移:
x位移保持在0.0x(x<5)
y位移保持在0.00x(x<5)
相机的运动周期:
x保持在0.0x (x<9)
y保持在0.0x/2 (x<9)
不运动的时候要将相机缓慢放回到原来的位置 , Lerp即可
直接看代码
cs
private Vector3 originalCameraPos;
[Header("摇晃强度")]
public float shakeIntensity = 0.05f;
[Header("摇晃频率")]
public float shakeFrequency = 10f;
void CameraShake()
{
if (xZ.magnitude != 0)
{
float shakeOffsetX = Mathf.Sin(Time.time * shakeFrequency) * shakeIntensity;
//float shakeOffsetY = Mathf.Cos(Time.time * shakeFrequency * 2f) * shakeIntensity * 0.5f;
HeadCamera.transform.localPosition = originalCameraPos + new Vector3(shakeOffsetX, 0f, 0f);
}
else
{
HeadCamera.transform.localPosition = Vector3.Lerp(HeadCamera.transform.localPosition, originalCameraPos, Time.deltaTime * 10f);
}
}
4.代码总览
![](https://i-blog.csdnimg.cn/direct/2742fc00401f48d5914a935bc1741138.gif)
cs
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.XR;
using static UnityEditor.Searcher.SearcherWindow.Alignment;
public class CCLearn : MonoBehaviour {
private Camera HeadCamera;
private CharacterController Controller;
#region 玩家视角
public Vector2 xY;
[Header("鼠标灵敏度")]
public float mouseSensitivity = 1.0f;
private Vector2 VirticalPersective = new Vector2(-90, 90);
private float currentVerticalAngle;
#endregion
#region 玩家移动
private Vector2 xZ;
Vector3 moveDirection;
[Header("移动速度")]
public float moveSpeed = 3.0f;
[Header("跑步速度")]
public float runSpeed = 6.0f;
#endregion
#region 摄像机摇晃
private Vector3 originalCameraPos;
[Header("摇晃强度")]
public float shakeIntensity = 0.05f;
[Header("摇晃频率")]
public float shakeFrequency = 10f;
#endregion
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start() {
Controller = GetComponent<CharacterController>();
HeadCamera = transform.Find("Head").GetComponentInChildren<Camera>();
HideMouse();
}
private void HideMouse(){
// 锁定鼠标光标并隐藏
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
// Update is called once per frame
void Update() {
PersPactive();
Move();
CameraShake();
Debug.Log(Time.deltaTime);
}
private void PersPactive() {
xY = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * mouseSensitivity;
currentVerticalAngle -= xY.y;
currentVerticalAngle = Mathf.Clamp(currentVerticalAngle, VirticalPersective.x, VirticalPersective.y);
//相机上下视角
HeadCamera.transform.localRotation = Quaternion.Euler(currentVerticalAngle, 0f, 0f);
//角色旋转
this.transform.Rotate(Vector3.up *xY.x);
}
private void Move(){
xZ = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
moveDirection = (transform.forward * xZ.y + transform.right * xZ.x).normalized * moveSpeed *Time.deltaTime;
if (Input.GetKey(KeyCode.LeftShift)) {
moveDirection = (transform.forward * xZ.y + transform.right * xZ.x).normalized * runSpeed * Time.deltaTime;
}
Controller.Move(moveDirection);
}
void CameraShake()
{
if (xZ.magnitude != 0)
{
float shakeOffsetX = Mathf.Sin(Time.time * shakeFrequency) * shakeIntensity;
float shakeOffsetY = Mathf.Cos(Time.time * shakeFrequency * 2f) * shakeIntensity * 0.5f;
HeadCamera.transform.localPosition = originalCameraPos + new Vector3(shakeOffsetX, shakeOffsetY, 0f);
}
else
{
HeadCamera.transform.localPosition = Vector3.Lerp(HeadCamera.transform.localPosition, originalCameraPos, Time.deltaTime * 10f);
}
}
}