【unity实战】Cinemachine虚拟相机+Character Controller实现俯视角、第三人称角色控制,复制粘贴即用

news2024/11/28 3:10:15

最终效果

在这里插入图片描述

文章目录

  • 最终效果
  • 一、前言
  • 二、Character Controller参数介绍
  • 三、添加虚拟相机
  • 四、2.5D俯视角人物操作
  • 五、自带重力的SimpleMove 移动
  • 六、第三人称角色控制
    • 1、移动
    • 2、添加重力
    • 3、 加地面检测,限制在地面重力不要累加
      • 3.1、自定义球形区域检测
      • 3.2、使用isGrounded判断是否接触地面
    • 4、跳跃
    • 5、物理碰撞效果
    • 6、最终代码
  • 七、下蹲
  • 完结

一、前言

前面其实已经做过使用CharacterController实现过第一人称的角色控制器:
【unity小技巧】unity最完美的CharacterController 3d角色控制器,实现移动、跳跃、下蹲、奔跑、上下坡、物理碰撞效果,复制粘贴即用

但是我发现很多人还是不理解,都跑来私信问我,所以决定重新做一个更加详细且简单控制器,主要是实现俯视角和第三人称角色控制。

二、Character Controller参数介绍

添加一个胶囊体,添加Character Controller组件,记得删除Capsule Collider组件,因为Character Controller自带了碰撞,所以不需要
在这里插入图片描述
Character Controller参数介绍
在这里插入图片描述

Slope Limit 可以爬坡的角度

Step Offset 能上的楼梯阶梯高度

skin width 相当于在CharacterController自己的碰撞体外再添加了一个与碰撞角度相关的碰撞体,skinwith可以消除抖动并且防止角色卡住。这里的建议是最好让你的skinwith的值至少大于0.01并且大于控制器半径的10%,但是调整了这个值可能会导致角色的脚无法触及地面,记得同时把碰撞体重心的位置向上调整一点点就可以了

Min Move Distance 最小的移动距离是指的是低于这个数值的移动会被系统忽略掉,我们一般设置为0

Center、Radius、Height其实就是定义碰撞体范围,中心位置、半径、高

三、添加虚拟相机

参考:【推荐100个unity插件之10】Unity最全的最详细的Cinemachine(虚拟相机系统)介绍,详细案例讲解,快速上手
在这里插入图片描述

四、2.5D俯视角人物操作

代码

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private CharacterController controller; // 角色控制器组件的引用
	public float Speed = 0.1f; // 玩家移动的速度
	float horizontal;
    float vertical;
    
	void Start()
    {
        controller = GetComponent<CharacterController>(); // 初始化角色控制器
    }
    
	void Update()
    {
        // 获取水平和垂直轴的输入
        horizontal = Input.GetAxis("Horizontal");
        vertical = Input.GetAxis("Vertical");
		
		MoveLikeTopDown();
    }

    private void MoveLikeTopDown()
    {
        // 获取水平和垂直轴的输入
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        // 根据输入创建移动方向向量,并标准化
        Vector3 direction = new Vector3(horizontal, 0, vertical).normalized;

        // 计算移动向量并根据速度和时间更新位置
        Vector3 move = direction * Speed * Time.deltaTime;
        controller.Move(move);

        // 将玩家的位置从世界坐标转换为屏幕坐标
        Vector3 playerScreenPoint = Camera.main.WorldToScreenPoint(transform.position);

        // 计算鼠标相对于玩家的偏移量
        Vector3 point = Input.mousePosition - playerScreenPoint;

        // 计算与鼠标位置的角度
        float angle = Mathf.Atan2(point.x, point.y) * Mathf.Rad2Deg;

        // 更新玩家的朝向,使其面向鼠标
        transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle, transform.eulerAngles.z);
    }
}

配置
在这里插入图片描述

相机参数改一下,改成俯视角,拉高相机
在这里插入图片描述
效果
在这里插入图片描述

五、自带重力的SimpleMove 移动

SimpleMove 方法是 CharacterController 组件提供的一个简化移动方式,它会自动处理重力,并且更适合不需要手动处理重力的场景。使用 SimpleMove 方法可以让代码更简洁,因为它自动处理了重力的应用。相比之下,Move 方法则要求你手动计算和应用重力。因此,如果你的角色只需要基本的移动而不需要额外的物理处理,SimpleMove 是一个更方便的选择。

但是SimpleMove 方法本身不支持跳跃。SimpleMove 自动处理重力,但不提供直接的方式来控制跳跃或其他复杂的运动行为。如果你需要实现跳跃功能,你应该使用 Move 方法,并手动管理重力和跳跃逻辑。

void MoveLikeWow()
{
    float horizontal = Input.GetAxis("Horizontal");
    float vertical = Input.GetAxis("Vertical");
    Vector3 move = transform.forward * Speed * vertical;

    // 使用 SimpleMove 方法,它会自动处理重力
    controller.SimpleMove(move);

    // 旋转角色
    transform.Rotate(Vector3.up, horizontal * RotateSpeed);
}

效果,可以看到角色已经有了重力
在这里插入图片描述

六、第三人称角色控制

如前面所说,如果你的游戏不需要角色进行跳跃且对重力定制性不高功能,则使用前面的SimpleMove确实是一种很好的方法。
如果我们角色需要跳跃等复杂的功能时,

1、移动

ws控制人物前后移动,用A和D来操作人物的旋转

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private CharacterController controller; // 角色控制器组件的引用
    public float Speed = 0.1f; // 玩家移动的速度
    public float RotateSpeed = 1f; // 玩家旋转的速度
    float horizontal;
    float vertical;

    void Start()
    {
        controller = GetComponent<CharacterController>(); // 初始化角色控制器
    }

    void Update()
    {
        // 获取水平和垂直轴的输入
        horizontal = Input.GetAxis("Horizontal");
        vertical = Input.GetAxis("Vertical");

		MoveLikeWow();
    }

    private void MoveLikeWow()
    {
        // 根据输入和速度计算移动方向
        Vector3 move = transform.forward * Speed * vertical;
        
        // 使用角色控制器移动角色
        controller.Move(move);

        // 根据水平输入旋转角色
        transform.Rotate(Vector3.up, horizontal * RotateSpeed);
    }
}

绑定相机跟随看向主角,并修改相机视角看向角色的头部
在这里插入图片描述

效果
在这里插入图片描述

相机拐弯有点抖动问题,把这个水平方向的这个震荡值调成零
在这里插入图片描述

效果
在这里插入图片描述

2、添加重力

注意controller.move调用多次的话,这里面的位移是可以互相叠加的,所以就是最终就是水平位移加上重力,不用担心下面move的会覆盖前面的move

public float Gravity = 19.8f;//重力
private Vector3 Velocity = Vector3.zero; // 角色的当前速度

// 更新重力影响的速度
Velocity.y += Gravity * Time.deltaTime;
// 使用角色控制器应用重力的移动
controller.Move(Velocity * Time.deltaTime);

效果
在这里插入图片描述

3、 加地面检测,限制在地面重力不要累加

可以看到,目前角色下落很快,那是因为目前游戏,角色重力就是一直累加,我们先加地面检测,地面检测其实有两种办法

3.1、自定义球形区域检测

你可以选择自己在角色脚下放置一个球形检测区域做地面检测,就像下面这样

[Header("地面检测")]
public bool IsGround;
public Transform GroundCheck;
public float CheckRadius = 0.2f;
public LayerMask layerMask;

//Physics.CheckSphere 用于检测一个球形区域是否与指定的碰撞层发生接触
IsGround = Physics.CheckSphere(GroundCheck.position, CheckRadius, layerMask);

但是这样通常会有一个问题,就是当角色处于悬崖边缘时,检测很容易不准确,导致跳不起来。
当然你可以旋转不使用球形检测,改用其他的检测进行优化,但是这无疑会增加很多的工作量。

3.2、使用isGrounded判断是否接触地面

CharactorController自带有isGrounded函数可以判断是否接触地面
在这里插入图片描述
但是它可不像官方文档说的这么简单,如果不懂如何正常使用的话,它存在很多坑,比如明明角色在地面上但是"CharacterController.isGrounded的值总是为false",或者"CharacterController.isGrounded的值在true和false反复横跳"。

如果你查看CharacterController.isGrounded会发现有一个段描述如下
在这里插入图片描述
其实际意思是上一次调用CharacterController.Move时CharacterController是否接触到了地面?

所以我们可以总结出isGrounded正确进行地面检测的两个条件:

  • CharacterController.isGrounded的地面检测判断之前一定要先执行CharacterController.Move方法
  • 你必须一直为它施加一个向下的速度,且速度不能为0

代码实现如下,注意当isGrounded为true时,将Y轴方向的速度设为了一个较小的负值(这里我设置的-2),这是为了确保能稳定触碰地面,避免isGrounded误判为false

// 应用重力
controller.Move(velocity * Time.fixedDeltaTime);
//地面检测
isGround = controller.isGrounded;
if (isGround)
{
    velocity.y = -2f;
}else{
    // 重力累加
    velocity.y += Gravity * Time.fixedDeltaTime;
}

4、跳跃

其实也就是在垂直方向施加一个力,这里有一个通用的近似物理效果的一个运动公式
在这里插入图片描述

// 跳跃处理
if (isGround && Input.GetButtonDown("Jump"))
{
    velocity.y = Mathf.Sqrt(JumpHeight * -2 * Gravity);
}

效果
在这里插入图片描述

5、物理碰撞效果

Character Controller本身不会对力作出反应,也不会自动推开刚体。

如果要通过角色控制器来推动刚体或对象,可以编写脚本通过 OnControllerColliderHit() 函数对与控制器碰撞的任何对象施力。

另一方面,如果希望玩家角色受到物理组件的影响,那么可能更适合使用rigidbody,而不是Character Controller

 //物理碰撞
private void OnControllerColliderHit(ControllerColliderHit hit)
{
    //获取碰撞体上的 Rigidbody 组件。
    Rigidbody body = hit.rigidbody;
    
    //检查获取到的 Rigidbody 是否为空或者是否为静态物体(isKinematic)
    if (body == null || body.isKinematic) return;

    // 我们不想把物体推到我们下面 
    if (hit.moveDirection.y < -0.3) return;

    //根据移动方向计算推动方向,
    //我们只将物体推向两侧,从不上下 
    Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);

    // 在碰撞点施加冲量 ForceMode.Impulse:添加一个瞬间的冲击力到刚体,且自动应用它的质量
    body.AddForceAtPosition(pushDir * 0.1f, hit.point, ForceMode.Impulse);
}

效果
在这里插入图片描述

6、最终代码

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private CharacterController characterController;
    float horizontal;
    float vertical;
    
    [Header("移动")]
    public float Speed = 10f; // 玩家移动的速度
    public Vector3 velocity = Vector3.zero; // 角色的当前速度

    [Header("旋转")]
    public float RotateSpeed = 30f; // 玩家旋转的速度

    [Header("跳跃")]
    public float JumpHeight = 3f;//跳跃高度
    public float Gravity = -39.8f;//重力

    [Header("地面检测")]
    public bool isGround;

    void Start()
    {
        characterController = GetComponent<CharacterController>(); // 初始化角色控制器
    }

    void Update()
    {
        // 获取输入
        horizontal = Input.GetAxis("Horizontal");
        vertical = Input.GetAxis("Vertical");

        // 处理角色旋转
        if (horizontal != 0)
        {
            transform.Rotate(Vector3.up * horizontal * RotateSpeed * Time.deltaTime);
        }

        // 处理角色移动
        Vector3 move = transform.forward * Speed * vertical;
        m_CollisionFlags = characterController.Move(move * Time.deltaTime);

        // 跳跃处理
        if (isGround && Input.GetButtonDown("Jump"))
        {
            velocity.y = Mathf.Sqrt(JumpHeight * -2 * Gravity);
        }

        // 应用重力
        characterController.Move(velocity * Time.deltaTime);
        //地面检测
        isGround = characterController.isGrounded;
        if (isGround)
        {
            // 当isGrounded为true时,将Y轴方向的速度设为了一个较小的负值,这是为了确保能稳定触碰地面,避免isGrounded误判为false
            velocity.y = -2f;
        }
        else
        {
            // 更新重力影响的速度
            velocity.y += Gravity * Time.deltaTime;
        }
    }

    //物理碰撞
    private void OnControllerColliderHit(ControllerColliderHit hit)
    {
        //获取碰撞体上的 Rigidbody 组件。
        Rigidbody body = hit.rigidbody;
        
        //检查获取到的 Rigidbody 是否为空或者是否为静态物体(isKinematic)
        if (body == null || body.isKinematic) return;

        // 我们不想把物体推到我们下面 
        if (hit.moveDirection.y < -0.3) return;

        //根据移动方向计算推动方向,
        //我们只将物体推向两侧,从不上下 
        Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);

        // 在碰撞点施加冲量 ForceMode.Impulse:添加一个瞬间的冲击力到刚体,且自动应用它的质量
        body.AddForceAtPosition(pushDir * 0.1f, hit.point, ForceMode.Impulse);
    }
}

七、下蹲

下蹲的逻辑就是让CharacterController 的高度减半,还有中心点的位置也跟着减半,当然还有摄像机的高度,还需要注意的是人物如果头顶有东西的时候我们是不允许他起立的,不然会穿模,所以还需要一个头顶检测,头顶我们使用盒子检测最好,可以覆盖整个头部

参考:https://blog.csdn.net/qq_36303853/article/details/134984516

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2087116.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MMGPL: 基于图提示学习的多模态医学数据分析 文献速递-大模型与多模态诊断阿尔茨海默症与帕金森疾病应用

Title 题目 MMGPL: Multimodal Medical Data Analysis with Graph Prompt Learning MMGPL: 基于图提示学习的多模态医学数据分析 01 文献速递介绍 神经系统疾病&#xff0c;包括自闭症谱系障碍&#xff08;ASD&#xff09;&#xff08;Lord 等&#xff0c;2018&#xff09…

Socket【网络】

文章目录 源端口号和目的端口号端口号&&进程pid TCP协议和UDP协议网络字节序socket 接口sockaddrsocket代码 源端口号和目的端口号 端口号&#xff1a; 端口号是传输层协议的内容。端口号是一个2字节16位的整数。端口号用来标识一个进程&#xff0c;告诉操作系统&…

【SpringCloud应用框架】GateWay网关

Spring Cloud Alibaba 之初识GateWay网关 文章目录 一、网关介绍二、网关对比三、GateWay基本概念&#xff1a;执行流程&#xff1a; 总结 一、网关介绍 在微服务架构中&#xff0c;一个系统会被拆分为多个微服务。如果没有网关存在&#xff0c;我们只能在客户端记录梅哥为服务…

Windows下创建FTP服务器,实现文件共享

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 创建FTP服务器 在【启用和关闭 Windows 功能】中选中【FTP服务】【IIS管理控制台】 应用更改完成后&#xff0c;重启电脑。 执行 ipconfig 命令查看当前电脑…

LeetCode刷题:3.无重复字符的最长子串

问题&#xff1a;首先分析问题得出需求 1.要求得到一个唯一最长子串的序列的长度。 子串&#xff1a;依据其形式是拥有一段长度的&#xff0c;所以考虑滑动窗口 唯一&#xff1a;考虑使用HashSet 需求描述&#xff1a;要求得到滑动窗口的大小&#xff0c;也就是左右指针的距离&…

STC89C52 串口浅谈

文章目录 1. 串口1.1 串口概述1.2 串口相关概念1.3 UART1.4 STC89C52串口1.4.1 串口相关寄存器1.4.2 串口模式图 1.5 串口配置简单代码示例1.5.1 UART串口初始化1.5.2 串口中断函数模板 1. 串口 1.1 串口概述 串口&#xff08;serial&#xff09;是一种通讯接口&#xff0c;可…

如何在Mac电脑上本地部署Stable Diffusion:详细教程(webUI)

Stable Diffusion是一款强大的AI生成图像模型&#xff0c;它可以基于文本描述生成高质量的图像。对于想要在本地运行此模型的用户来说&#xff0c;使用Mac电脑部署Stable Diffusion是一个非常吸引人的选择&#xff0c;特别是对于M1或M2芯片的用户。本文将详细介绍如何在Mac上本…

【图像去噪】论文精读:Spatial-Adaptive Network for Single Image Denoising(SADNet)

请先看【专栏介绍文章】&#xff1a;【图像去噪&#xff08;Image Denoising&#xff09;】关于【图像去噪】专栏的相关说明&#xff0c;包含适配人群、专栏简介、专栏亮点、阅读方法、定价理由、品质承诺、关于更新、去噪概述、文章目录、资料汇总、问题汇总&#xff08;更新中…

数据结构(邓俊辉)学习笔记】串 15——BM_GS算法:综合性能

1.BM之性能 接下来&#xff0c;针对已经综合了 bc 和 gs 两种策略的 BM 算法&#xff0c;标定它对应的复杂度&#xff0c;并将这种算法与此前的 KMP 以及蛮力算法在性能上做一个综合的对比分析。 首先是 BM 算法本身的性能。 在空间方面&#xff0c;除了模式串和文本串本身&a…

Qt:玩转QPainter序列十

前言 最后的一个解读QPainter源码的系列&#xff0c;剩下的源码全部都是内联函数&#xff0c;自己看看就好。 正文 渲染相关 void setRenderHint(RenderHint hint, bool on true); 功能: 启用或禁用特定的渲染提示。参数: hint&#xff1a;指定要设置的渲染提示&#xff0c;…

基于Leaflet Legend的图例数据筛选实践-以某市教培时空分布为例

目录 前言 一、关于Leaflet.Legend组件 1、Legend组件的主要参数 2、相关参数 二、Legend图例可视化控制 1、违规教培信息的管理 2、违规培训信息时空可视化及图例渲染控制 3、成果展示 三、总结 前言 在很多的地理时空分析系统中&#xff0c;我们经常会遇到一些需求。…

运维-3.分库分表

分库分表 1.介绍2.Mycat概述3.Mycat入门4.Mycat配置4.1 schema.xml4.1.1 schema标签4.1.2 dataNode标签4.1.3 dataHost标签 4.2 rule.xml4.3 server.xml4.3.1 system标签4.3.2 user标签 5.Mycat分片5.1 垂直拆分5.2 水平拆分5.3 分片规则5.3.1 范围分片5.3.2 取模分片5.3.3 一致…

若依微服务Admin控制台不显示ruoyi-file问题解决

本地启动完若依微服务,发现Admin控制台只显示了6个服务,其中ruoyi-file启动成功,但是没有在Admin控制台中显示处理,本章问题,给出这个问题的解决办法。 一、什么是服务监控 监视当前系统应用状态、内存、线程、堆栈、日志等等相关信息,主要目的在服务出现问题或者快要出…

机器学习和深度学习·贝叶斯优化和optuna

贝叶斯优化 贝叶斯优化的思想 先验&#xff1a;取点 似然&#xff1a;假设分布 取了n个点之后… 后验&#xff1a;近似取得极值 贝叶斯优化的数学过程 在贝叶斯优化的数学过程当中&#xff0c;我们主要执行以下几个步骤&#xff1a; 1 定义需要估计的 f ( x ) f(x) f(x)以及…

零基础学习Python(七)

1. 字符串常用方法 lower()、upper()&#xff1a;转换为小写字符串、大写字符串 split(str)&#xff1a;按照指定字符串str进行分割&#xff0c;结果为列表&#xff1a; email "123qq.com" print(email.split("")) [123, qq.com] count(str)&#xf…

MySQL 系统库

文章目录 一. 概念二. performance_schema1. 概念 2. 检查当前数据库版本是否支持3. performance_schema表分类5. 配置与使用6. 查看执行失败的SQL语句7. 查看最近事务执行信息8. 小结 三. sys系统库1. 使用须知2. 使用3. 查看慢SQL语句慢在哪4. 小结 四. Information_schema1.…

中国城市经济韧性数据集(2007-2022年)

数据来源&#xff1a;数据来自历年《中国城市统计NJ》、各省市《统计NJ》及《中国区域经济统计NJ》 时间范围&#xff1a;2007-2022年 数据范围&#xff1a;中国地级市样例数据&#xff1a; 包含内容&#xff1a; 全部内容下载链接&#xff08;原始数据计算代码最终数据&…

Spring Boot DevTools:简化开发,实现热部署

Spring Boot DevTools&#xff1a;简化开发&#xff0c;实现热部署 1、如何集成&#xff1f;MavenGradle 2、主要特性3、注意事项 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Spring Boot DevTools是开发者的得力助手&#xff0c;它通过…

AR 眼镜之-系统通知定制(通知弹窗)-实现方案

目录 &#x1f4c2; 前言 AR 眼镜系统版本 系统通知定制 1. &#x1f531; 技术方案 1.1 技术方案概述 1.2 实现方案 1&#xff09;实现系统通知的监听 2&#xff09;系统通知显示&#xff1a;通知弹窗 2. &#x1f4a0; 实现系统通知的监听 2.1 继承 NotificationLi…

全场景——(六)Moubus 功能码详解

文章目录 一、功能码概要二、&#xff08;0x01&#xff09;读取线圈2.1 功能说明2.2 查询报文2.3 响应报文 三、 (0x02) 读取离散量输入值3.1 功能说明3.2 查询报文3.3 响应报文 三、&#xff08;0x03&#xff09;读取保持寄存器值3.1 功能说明3.2 查询报文3.3 响应报文 五、 (…