[Unity角色控制专题] 详细说明如何使用Character Controller配合脚本实现类似MC的第一人称控制(仅移动与视角摇晃)

关于角色控制器的基本用法我就不做介绍了,请自行查看相关文档:

Unity - Manual: Character Controller component reference

本文用到了三角函数和插值函数,非常简单,如有疑问请查询以下文章:

Unity中的数学 之 Mathf_unity mathf-CSDN博客

Unity中的数学应用 之 插值函数处理角色朝向 (初中难度 +Matlab)-CSDN博客

首先来看实现效果

目录

0.玩家与组件基础

基础场景

结构关系

实际图

组件一览

辅助功能:

基本变量

1.玩家的视角钳制

1.视野分析

[2. 旋转轴](#2. 旋转轴)

3.变量一览

4.功能实现

2.玩家的移动控制

3.摄像头的摇晃

4.代码总览


0.玩家与组件基础

基础场景

结构关系

第一人称视角,主摄像机在玩家的头部做子对象没什么好说的

实际图

组件一览

这里要注意,Character Controller本身就是一个另类的碰撞器 ,所以不要再加capsule collider了,但可以添加刚体组件,因为碰撞器只提供碰撞/触发检测,不能模拟物理效果

最后一个脚本是我们本次要编写的

辅助功能:

开发者/用户鼠标隐藏:

cs 复制代码
    private void HideMouse(){
        // 锁定鼠标光标并隐藏
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

简单准心

基本变量

cs 复制代码
    private Camera HeadCamera;
    private CharacterController Controller;

1.玩家的视角钳制

首先来看我们实现控制视角的时候都需要注意什么地方:

1.视野分析

人的头能抬起和看下的角度不同,还有眼睛的转动配合 会导致视野情况因人而异(大致在-90°到90°),因此我们直接不考虑眼球转动 直接让玩家的视野钳制在±90°即可

横向我们就不考虑了直接做360°旋转即可也就是±180 不做任何钳制

2. 旋转轴

注意加的方向,这里有一个点是比较容易忽略的**,我们角色向下看是对x值做加法**

但是我们的十字轴 向下拖动是负方向

所以在代码之中,要注意这一点

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.摄像头的摇晃

我们只需要分析一下相机应该怎么动

假设这个是相机:

想要其做简谐运动第一时间想到的肯定是三角函数

三角函数必然要有一个连续的输入所以输入值 也就是三角函数的参数取一个Time.time作为横轴移动

但是单纯的左右或上下移动摄像机只会很突兀就比如下面这样:

单纯x跟蛇一样

单纯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.代码总览

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);
    }
}
}
相关推荐
C666688836 分钟前
同步(Synchronous)和异步(Asynchronous)
开发语言·c#
yyhnet.cn3 小时前
在cursor/vscode中使用godot C#进行游戏开发
vscode·c#·godot·cursor
来恩10039 小时前
C# 线程与同步介绍
java·redis·c#
GesLuck10 小时前
C#控件开发6—旋转按钮
开发语言·javascript·c#
User:你的影子11 小时前
WPF进度条渲染
前端·javascript·c#·wpf
csdn_aspnet13 小时前
ASP.NET Core 如何使用 C# 从端点发出 GET 请求
c#·asp.net·.netcore
朴拙数科14 小时前
如何使用 Python 高效操作 Word 文档:python-docx 和 comtypes 介绍与实践
python·c#·word
qq_4071109214 小时前
OpenOffice实现word转pdf
pdf·c#·word
奔波小哥14 小时前
如何在电脑后台定时进行自动截图?自动截图后如何快捷保存?如何远程查看?
c#
xiaowu08017 小时前
C# 比较两个List集合内容是否相同
windows·c#·list