Unity3D学习FPS游戏(4)重力模拟和角色跳跃

news2024/10/25 10:09:13

前言:前面两篇文章,已经实现了角色的移动和视角转动,但是角色并没有办法跳跃,有时候还会随着视角移动跑到天上。这是因为缺少重力系统,本篇将实现重力和角色跳跃功能。觉得有帮助的话可以点赞收藏支持一下!

重力模拟和角色跳跃

  • 问题-太空人
  • 地面检测
  • 重力模拟
  • 跳跃
  • 完整代码
  • 效果
  • 补充知识
    • Property Attributes自定义属性
    • Time.fixedDeltaTime

问题-太空人

回到我们第二篇讲角色控制的三种方法中,提到了CharacterController虽然有碰撞但是是没有力的作用的,也就意味着没有重力效果。

在这里插入图片描述

地面检测

可以利用CharacterController.isGrounded来判断是否碰到地面。

但是要注意一下函数的功能定义:

isGrounded: Was the CharacterController touching the ground during the last move?

意思是判断的是上一次调用CharacterController.Move时有没有触地。

如果遇到isGrounded如果恒为false可能要注意,判断之前是不是还没有调用过Move。

重力模拟

非常简单,利用学过物理基本的公式a*t=v(加速度*时间=速度)、v*t=s(速度*时间=距离),就可以模拟出来。

新增重力加速度playerGravity和垂直速度verticalVelocity,当碰到地面时候垂直速度是0;当没有碰到地面的时候,垂直速度会随着受力一直变化,速度+=加速度*单位时间。在空中只有重力,对应代码就是verticalVelocity-=playerGravity*Time.fixedDeltaTime,为什么是减号因为重力是向下的,有方向。

最后把因为重力导致位移变化也一起Move就可以了。

public float playerGravity = 9.8F;// 重力加速度
public float verticalVelocity = 0;// 垂直速度
public void PlayerMovement()
{
    Vector3 moveDirection = Vector3.zero;
    // 获取键盘输入:Horizontal左右移动;Vertical前后移动
    // 加入自身tranform方向
    moveDirection = Vector3.zero;
    moveDirection += transform.forward  * Input.GetAxis("Vertical");
    moveDirection += transform.right * Input.GetAxis("Horizontal");
    moveDirection *= Time.deltaTime * moveSpeed;
    // 重力
    if (!m_Controller.isGrounded)
        verticalVelocity -= playerGravity * Time.fixedDeltaTime;// a*t=v
    else
        verticalVelocity = 0.0F;
    moveDirection.y += verticalVelocity * Time.fixedDeltaTime;// v*t=s
    // 移动
    m_Controller.Move(moveDirection);
}

由于涉及到了物理模拟,所以时间增量都改用了Time.fixedDeltaTime,并在FixedUpdate更新。这个使用Time.fixedDeltaTime具体原因在补充知识中有详细说明。

跳跃

有了重力,跳跃才有作用。

简单跳跃

跳跃的思路,首先不能多段跳,就是要碰到地面才能响应跳跃按键,跳跃后角色会上升一定高度。

添加一个玩家跳跃高度参数jumpHeight,直接让角色位移到jumpHeight,就能实现一个简单的跳跃效果

public float jumpHeight = 2F;
public void PlayerMovement()
{
    Vector3 moveDirection = Vector3.zero;
    // 获取键盘输入:Horizontal左右移动;Vertical前后移动
    // 加入自身tranform方向
    moveDirection = Vector3.zero;
    moveDirection += transform.forward  * Input.GetAxis("Vertical");
    moveDirection += transform.right * Input.GetAxis("Horizontal");
    moveDirection *= Time.deltaTime * moveSpeed;
    // 重力
    if (!m_Controller.isGrounded)
        verticalVelocity -= playerGravity * Time.fixedDeltaTime;// a*t=v
    else
        verticalVelocity = 0.0F;
    moveDirection.y += verticalVelocity * Time.fixedDeltaTime;// v*t=s
    // 跳跃
    if (Input.GetButton("Jump"))
    {
        // 得在地面才能跳
        if (m_Controller.isGrounded)
        {
           moveDirection.y += jumpHeight;
        }
    }
    // 移动
    m_Controller.Move(moveDirection);
}

跳跃优化

上面简单的跳跃,虽然能够实现跳跃功能,但是非常不自然,就是一下子跳到最高,又慢慢下来。

实际游戏中,我们的跳跃真正其实是一个抛物线。

在这里插入图片描述

也就向上跳到最顶的时候,也有个速度变化。

跳起来的时候会有一个向上的速度,之后不触地都是只受到重力作用了。所以点击跳跃按钮后,我们只需要算出这个跳起来一瞬间的向上速度是多少就行了。

还是最基础的物理公式推导:
a*t=v(加速度*时间=速度)、v*t=s(速度*时间=距离),设定跳跃的最高的高度h(jumpHeight)。
从最低点到最高点,通过h=0.5*a*t2,可以把总共的时长算出来,由于受力只有重力,通过重力加速度*总时长=初始向上速度。

verticalVelocity = Mathf.Sqrt(jumpHeight * 2 / playerGravity) * playerGravity;// 初始向上速度

于是新的代码就是:

public float jumpHeight = 2F;
public float verticalVelocity = 0;// 垂直速度
public void PlayerMovement()
{
    Vector3 moveDirection = Vector3.zero;
    // 获取键盘输入:Horizontal左右移动;Vertical前后移动
    // 加入自身tranform方向
    moveDirection = Vector3.zero;
    moveDirection += transform.forward  * Input.GetAxis("Vertical");
    moveDirection += transform.right * Input.GetAxis("Horizontal");
    moveDirection *= Time.fixedDeltaTime * moveSpeed;
    // 重力
    if (!m_Controller.isGrounded)
        verticalVelocity -= playerGravity * Time.fixedDeltaTime;// a*t=v
    else
        verticalVelocity = 0.0F;
    // 跳跃
    if (Input.GetButton("Jump"))
    {
        // 得在地面才能跳
        if (m_Controller.isGrounded)
        {
            verticalVelocity = Mathf.Sqrt(jumpHeight * 2 / playerGravity) * playerGravity;// 初始向上速度
        }
    }
    moveDirection.y += verticalVelocity * Time.fixedDeltaTime;// v*t=s
    // 移动
    m_Controller.Move(moveDirection);
}

完整代码

具有角色移动、视角、重力和跳跃的完整PlayerController代码。

public class PlayerController : MonoBehaviour
{
    [Header("Unity组件")]
    public CharacterController m_Controller;
    public Transform playerCamera;

    [Header("玩家数值")]
    public float moveSpeed = 6.0F;
    public float mouseSensitivity = 1000.0F;
    public float playerGravity = 9.8F;
    public float jumpHeight = 2F;
    public float verticalVelocity = 0;// 垂直速度
    private float x_RotationOffset = 0;// 累计x旋转
    private float y_RotationOffset = 0;// 累计y旋转


    void Start()
    {
        m_Controller = this.GetComponent<CharacterController>();
        Cursor.lockState = CursorLockMode.Locked;// 鼠标锁定
    }

    
    private void FixedUpdate()
    {
        PlayerRotation();
        PlayerMovement();
    }

    // 控制角色移动
    public void PlayerMovement()
    {
        Vector3 moveDirection = Vector3.zero;
        // 获取键盘输入:Horizontal左右移动;Vertical前后移动
        // 加入自身tranform方向
        moveDirection = Vector3.zero;
        moveDirection += transform.forward  * Input.GetAxis("Vertical");
        moveDirection += transform.right * Input.GetAxis("Horizontal");
        moveDirection *= Time.fixedDeltaTime * moveSpeed;
        // 重力
        if (!m_Controller.isGrounded)
            verticalVelocity -= playerGravity * Time.fixedDeltaTime;// a*t=v
        else
            verticalVelocity = 0.0F;
        // 跳跃
        if (Input.GetButton("Jump"))
        {
            // 得在地面才能跳
            if (m_Controller.isGrounded)
            {
                verticalVelocity = Mathf.Sqrt(jumpHeight * 2 / playerGravity) * playerGravity;// 初始向上速度
            }
        }
        moveDirection.y += verticalVelocity * Time.fixedDeltaTime;// v*t=s
        // 移动
        m_Controller.Move(moveDirection);
    }

    // 控制角色视角
    private void PlayerRotation()
    {
        // 获取鼠标移动
        x_RotationOffset += Input.GetAxis("Mouse X")* Time.fixedDeltaTime * mouseSensitivity;// 水平方向
        y_RotationOffset += Input.GetAxis("Mouse Y")* Time.fixedDeltaTime * mouseSensitivity;// 垂直方向
        // 限制垂直旋转角度
        y_RotationOffset = Mathf.Clamp(y_RotationOffset, -45f, 45f);
        // 旋转
        transform.rotation = Quaternion.Euler(new Vector3(0, x_RotationOffset, 0));// Player旋转
        playerCamera.localRotation= Quaternion.Euler(new Vector3(y_RotationOffset, playerCamera.localEulerAngles.y, playerCamera.localEulerAngles.z));// Camera旋转
    }
}

效果

简单跳跃。
在这里插入图片描述

优化后的跳跃。
在这里插入图片描述

补充知识

Property Attributes自定义属性

我们写代码的时候,会写一些属性在Inspector中也能修改,方便查看不同值的表现效果。
例如我们现在PlayerController中的moveSpeed和playerGravity等,但是要定义为Public。

Property Attributes提供了很多内置属性,方便Inspector查看自定义的属性和修改。

常用的有:

  • [Header(“xxx”)]给变量在Inspector中添加标题头
  • [Space(xx)]在Inspector中添加间隔
  • [Range(xx, xx)]在Inspector中变量为滑动条范围
  • [Tooltip(“xxx”)]为变量在Inspector中添加说明(像可以在Inspector查看的注释)

里面的文本文字可以是中文。
有这些能让我们代码更加优雅😀,也方便和别人共事的时候更好阅读使用我们的代码。

Time.fixedDeltaTime

fixedDeltatime是一个固定的时间增量不会随着帧率变换而变化,要在FixedUpdate中使用。

通常来说fixedDeltatime固定是0.02s,在主菜单的Edit→Project Settings→Time可以修改。

之前我们用的帧率相关的增量时间,但是现在开始已经涉及到了物理模拟了,物理更新对时间是非常敏感的,稍微差一点都不行。如果用在按帧率更新,可能由于主机差异帧率不稳定,0.5秒子弹到达敌人,帧率不稳定可能会变成0.8s。

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

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

相关文章

社区养老实训室解决方案

一、实训室建设理念与目标 1.1 培养高质量养老专业人才 随着人口老龄化的不断加剧&#xff0c;对养老专业人才的需求呈现出日益增长的趋势。社区养老实训室的建设理念&#xff0c;正是基于这一背景&#xff0c;致力于培养一支既具备专业技能又拥有综合服务能力的高质量养老人…

gitlab不同账号间·仓库转移

背景&#xff1a;公司业务调整&#xff0c;原先在海外仓库的代码转移回国内 诉求&#xff1a;完整的保留项目记录 操作&#xff1a; 步骤一: 定位到需要迁移的原项目地址 步骤二&#xff1a;创建新项目 步骤三&#xff1a;打开命令行&#xff0c;创建好文件路径为需要clo…

Anchor DETR论文笔记

原文链接 [2109.07107] Anchor DETR: Query Design for Transformer-Based Object Detection (arxiv.org)https://arxiv.org/abs/2109.07107 原文笔记 What 提出了一种新的基于锚点的查询设计&#xff0c;即将锚点编码为对象查询。 Why 对象检测任务是预测图像中每个对象…

监督学习之逻辑回归

逻辑回归&#xff08;Logistic Regression&#xff09; 逻辑回归是一种用于二分类&#xff08;binary classification&#xff09;问题的统计模型。尽管其名称中有“回归”二字&#xff0c;但逻辑回归实际上用于分类任务。它的核心思想是通过将线性回归的输出映射到一个概率值…

C++与现代开发实践第三节:多线程与并发编程

第四章&#xff1a;C与现代开发实践 第三节&#xff1a;多线程与并发编程 在这一课中&#xff0c;我们将详细探讨多线程与并发编程的各个方面&#xff0c;特别是从线程的创建、管理到高级的优化技术&#xff0c;并且通过复杂的实战案例来展示如何应对并发问题。最后&#xff…

探索现代软件开发中的持续集成与持续交付(CI/CD)实践

探索现代软件开发中的持续集成与持续交付&#xff08;CI/CD&#xff09;实践 随着软件开发的飞速进步&#xff0c;现代开发团队已经从传统的开发模式向更加自动化和灵活的开发流程转变。持续集成&#xff08;CI&#xff09; 与 持续交付&#xff08;CD&#xff09; 成为当下主…

git入门操作

文章目录 git入门操作git创建仓库&#xff1a;git initgit clone工作区域&#xff1a;文件状态git添加和提交git add git statusgit add .git commit -m 版本描述git ls-filesgit log git的reset回退版本git log 查看版本号git reset --softgit reset --hardgit reset --mixed总…

Github 2024-10-21 开源项目周报 Top15

根据Github Trendings的统计,本周(2024-10-21统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目7Python项目5Go项目2Svelte项目1非开发语言项目1C++项目1Shell项目1技术面试必备知识开源项目 创建周期:2442 天Star数量:1762…

chrome清除https状态

莫名其妙的http跳转到https的url了。 解决办法 浏览器地址栏输入&#xff1a;chrome://net-internals/#hsts 输入你需要删除的域名即可&#xff01;&#xff01;&#xff01;

uniapp picker实现省市二级级联和省市区三级级联

接口返回值格式&#xff1a; 二级级联-vue2 <picker mode"multiSelector" change"bindPickerChange" columnchange"columnchange" :value"index":range"array" range-key"label"><view class"uni…

Qt (QGroupBox、QTableView、QTableWidget)QSS样式

文章目录 设置效果样式内容说明qss文件内容补充 设置效果 先上图&#xff0c;为了方便大家区分&#xff0c;使用了多种颜色进行设置。 样式内容说明 * {background-color: #88e7ea; }设置全局背景色 可能是因为 QGroupBox 的背景色优先级较高&#xff0c;覆盖了全局样式。 …

GD32学习知识点累计

时钟系统 GD32f427主频最高位240MHZ&#xff08;但是只能到200M&#xff09;&#xff0c;GD32给的函数外接25MHZ晶振配置主频为200MHZ,APB1最高频率为60HZ配置为主频的4分频为50MHZ&#xff0c;APB2最大为120MHZ配置为主频的2分频为100MHZ 定时器 无论什么定时器最大频率为200M…

上行流量和下行流量的区别

一、定义 上行流量 指从本地设备&#xff08;如用户的计算机、手机等客户端设备&#xff09;发送数据到远程设备&#xff08;如服务器&#xff09;的流量。简单来说&#xff0c;就是数据从你的设备传出去的过程所产生的流量。例如&#xff0c;当你上传一张图片到云存储服务时&…

Ansible 的脚本 --- playbooks剧本

playbooks 本身由以下各部分组成 &#xff08;1&#xff09;Tasks&#xff1a;任务&#xff0c;即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 &#xff08;2&#xff09;Vars&#xff1a;变量 &#xff08;3&#xff09;Templates&#xff1a;模板 &a…

虚拟化数据恢复——Hyper-V虚拟机文件丢失导致虚拟机无法使用的数据恢复案例

虚拟化数据恢复环境&#xff1a; Windows Server操作系统服务器上部署Hyper-V虚拟机环境。虚拟机的硬盘文件和配置文件存放在一台存储中&#xff0c;该存储上有一组由4块硬盘组建的raid5阵列&#xff0c;除此之外&#xff0c;还有一块单盘存放档虚拟机的备份文件。 虚拟化故障…

【ubuntu20.04】【ROS Noetic】【ROS安装】【Website may be down.】【gpg: 找不到有效的 OpenPGP 数据。】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、登入www.ros.org1.Setup your sources.list2.Set up your keys中间出了点问题 gpg: 找不到有效的 OpenPGP 数据。4.Installation下载安装ros5.环境参数的配…

使用js和canvas、html实现简单的俄罗斯方块小游戏

玩法介绍 点击开始游戏后&#xff0c;使用键盘上的←→控制移动&#xff0c;↑控制方块旋转&#xff0c;↓控制方块加速下落&#xff0c;累计一行即可消除并获得分数&#xff0c;触碰到顶部时游戏结束 代码实现 html代码复制即用&#xff0c;可阅读注释 <!DOCTYPE html…

(三)行为模式:11、模板模式(Template Pattern)(C++示例)

目录 1、模板模式含义 2、模板模式的UML图学习 3、模板模式的应用场景 4、模板模式的优缺点 5、C实现的实例 1、模板模式含义 模板模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个操作中的算法骨架&#xff0c;将某些步骤…

【软件工程】软件项目管理/工程项目管理复习资料

第一章 软件项目管理概述习题 一. 填空题 实现项目目标的制约因素有&#xff08; 项目范围 &#xff09;、&#xff08; 成本 &#xff09;、&#xff08; 进度计划 &#xff09;、&#xff08; 客户满意度 &#xff09;等。 项目管理&#xff08; 启动过程组 &#xff09;、…

使用Jenkins持续集成的一些经验总结

作为一名测试开发人员或工程师&#xff0c;您是否曾在项目中遇到手动部署与测试效率低下的问题&#xff1f;当每次提交代码都需要人工触发一系列的构建与测试流程时&#xff0c;整个开发进度都会受到拖累。正是在这样的背景下&#xff0c;Jenkins&#xff0c;作为持续集成的关键…