当前位置: 首页 > article >正文

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

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

        Unity - Manual: Character Controller component reference

        本文用到了三角函数和插值函数,非常简单,如有疑问请查询以下文章:
        Unity中的数学 之 Mathf_unity mathf-CSDN博客

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

 首先来看实现效果

目录

0.玩家与组件基础

基础场景

结构关系

实际图

组件一览

辅助功能:

 基本变量

1.玩家的视角钳制  

1.视野分析

2. 旋转轴

3.变量一览

4.功能实现 

2.玩家的移动控制

3.摄像头的摇晃

 4.代码总览


 

0.玩家与组件基础

基础场景

结构关系

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

实际图

组件一览

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

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

        

辅助功能:

        开发者/用户鼠标隐藏:

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

        简单准心 

         

 基本变量

    private Camera HeadCamera;
    private CharacterController Controller;

1.玩家的视角钳制  

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

1.视野分析

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

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

2. 旋转轴

        

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

        但是我们的十字轴 向下拖动是负方向   
        所以在代码之中,要注意这一点

3.变量一览

    public Vector2 xY;
    [Header("鼠标灵敏度")]
    public float mouseSensitivity = 1.0f;

    private Vector2 VirticalPersective = new Vector2(-90, 90);

    private float currentVerticalAngle;

4.功能实现 

    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(xY *xY.x);
    }

       需要注意的一点是,上下控制相机旋转,左右控制角色旋转,不要搞混

2.玩家的移动控制

        这个其实就没有什么好讲的直接看代码

    private Vector2 xZ;
    Vector3 moveDirection;
    [Header("移动速度")]
    public float moveSpeed = 3.0f;
    [Header("跑步速度")]
    public float runSpeed = 6.0f;
  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即可

      直接看代码

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

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);
    }
}
}

 

 

 

 

 


http://www.kler.cn/a/535157.html

相关文章:

  • Go 语言 | 入门 | 快速入门
  • PostgreSQL:字符串函数用法
  • WebSocket 实时通信详解:原理、应用与实践
  • DeepSeek写的lammps反应势断键动态显示程序
  • Mysql:数据库
  • 【自然语言处理(NLP)】NLP实战:IMDB影评情感分析项目
  • C++《AVL树》
  • 一文解释nn、nn.Module与nn.functional的用法与区别
  • 20250206在ubuntu20.04下使用unzip解压缩带中文名的文件
  • Golang的引用类型和指针
  • DeepSeek 多模态大模型Janus-Pro本地部署教程
  • 【教程】docker升级镜像
  • 《C#之集训1-20121019c#基础》
  • 【人工智能】使用deepseek初体验
  • 代码随想录算法训练营第四十四天-动态规划-子序列-392.判断子序列
  • OKHttp拦截器解析
  • OpenOffice实现word转pdf
  • 深度整理总结MySQL——行记录存储
  • tcpdump 的工作层次
  • 计算机网络笔记再战——理解几个经典的协议4
  • AI 编程工具—Cursor进阶使用 生成AI工作流
  • 售后板子HDMI无输出分析
  • 力扣.270. 最接近的二叉搜索树值(中序遍历思想)
  • 分析用户请求K8S里ingress-nginx提供的ingress流量路径
  • 服务器升级nginx版本
  • ImportError: cannot import name ‘Undefined‘ from ‘pydantic.fields‘