20240116-【UNITY 学习】增加滑动功能

替换脚本PlayerMovement_02.cs

在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement_03 : MonoBehaviour
{
    private float moveSpeed; // 玩家移动速度
    public float walkSpeed = 7; // 行走速度
    public float sprintSpeed = 10; // 冲刺速度
    public float slideSpeed = 30; // 滑动速度

    private float desiredMoveSpeed; // 期望的移动速度
    private float lastDesiredMoveSpeed; // 上一次的期望移动速度

    public float speedIncreaseMultiplier = 1.5f; // 速度增加倍数
    public float slopeIncreaseMultiplier = 2.5f; // 斜坡增加倍数

    public float groundDrag = 5; // 地面时的阻力

    public float playerHeight = 2; // 玩家身高
    public LayerMask whatIsGround; // 地面的LayerMask
    private bool grounded; // 是否在地面上

    public float jumpForce = 6; // 跳跃力度
    public float jumpCooldown = 0.25f; // 跳跃冷却时间
    public float airMultiplier = 0.4f; // 空中移动速度衰减
    private bool readyToJump = true; // 是否可以跳跃

    public float crouchSpeed = 3.5f; // 蹲伏时的移动速度
    public float crouchYScale = 0.5f; // 蹲伏时的Y轴缩放比例
    private float startYScale; // 初始Y轴缩放比例

    public float maxSlopAngle = 40; // 最大坡度角度
    private RaycastHit slopeHit; // 坡度检测的射线信息
    private bool exitingSlope = true; // 是否正在离开坡度

    public KeyCode jumpKey = KeyCode.Space; // 跳跃键
    public KeyCode sprintKey = KeyCode.LeftShift; // 冲刺键
    public KeyCode crouchKey = KeyCode.LeftControl; // 下蹲键

    public Transform orientation; // 玩家朝向的Transform

    private float h; // 水平输入
    private float v; // 垂直输入

    private Vector3 moveDirection; // 移动方向

    private Rigidbody rb; // 玩家刚体

    public MovementState state; // 当前玩家的移动状态
    public enum MovementState
    {
        walking,    // 行走
        sprinting,  // 冲刺
        crouching,  // 蹲伏
        sliding,    // 滑动
        air         // 空中
    }

    public bool sliding; // 是否正在滑动

    private void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.freezeRotation = true; // 防止刚体旋转

        startYScale = transform.localScale.y;  // 记录初始的Y轴缩放
    }

    private void Update()
    {
        grounded = Physics.Raycast(transform.position, Vector3.down, playerHeight * 0.5f + 0.2f, whatIsGround);

        MyInput();

        SpeedControl();

        StateHandler();

        if (grounded)
            rb.drag = groundDrag;
        else
            rb.drag = 0;
    }

    private void FixedUpdate()
    {
        MovePlayer();
    }

    private void MyInput()
    {
        // 获取水平和垂直输入
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        // 如果按下跳跃键且准备好跳,并且在地面上
        if (Input.GetKey(jumpKey) && readyToJump && grounded)
        {
            readyToJump = false;

            Jump();

            Invoke(nameof(ResetJump), jumpCooldown);
        }

        if (Input.GetKeyDown(crouchKey))
        {
            // 调整玩家缩放以模拟蹲下效果
            transform.localScale = new Vector3(transform.localScale.x, crouchYScale, transform.localScale.z);

            rb.AddForce(Vector3.down * 5f, ForceMode.Impulse);
        }

        // 如果释放下蹲键
        if (Input.GetKeyUp(crouchKey))
        {
            // 恢复到原始Y轴缩放
            transform.localScale = new Vector3(transform.localScale.x, startYScale, transform.localScale.z);
        }
    }

    private void MovePlayer()
    {
        // 根据朝向计算移动方向
        moveDirection = orientation.forward * v + orientation.right * h;

        // 如果在斜坡上并且不是即将离开斜坡
        if (OnSlope() && !exitingSlope)
        {
            // 在斜坡上施加力,以便更好地移动
            rb.AddForce(GetSlopeMoveDirection(moveDirection) * moveSpeed * 20f, ForceMode.Force);

            // 如果垂直速度为正(上升),则额外施加向下的力,以克服斜坡引起的垂直速度变慢
            if (rb.velocity.y > 0)
            {
                rb.AddForce(Vector3.down * 80f, ForceMode.Force);
            }
        }
        else if (grounded) // 如果在地面上
        {
            rb.AddForce(moveDirection.normalized * moveSpeed * 10f, ForceMode.Force); // 在地面上施加移动力
        }
        else if (!grounded) // 如果在空中
        {
            // 在空中施加移动力,乘以空中移动速度衰减系数
            rb.AddForce(moveDirection.normalized * moveSpeed * 10f * airMultiplier, ForceMode.Force);
        }

        // 根据是否在斜坡上决定是否启用重力
        rb.useGravity = !OnSlope();
    }

    private void SpeedControl()
    {
        // 如果在斜坡上并且不是即将离开斜坡
        if (OnSlope() && !exitingSlope)
        {
            // 如果速度的大小超过了设定的移动速度
            if (rb.velocity.magnitude > moveSpeed)
            {
                // 将速度归一化,并乘以设定的移动速度,以限制速度在设定范围内
                rb.velocity = rb.velocity.normalized * moveSpeed;
            }
        }
        // 如果不在斜坡上
        else
        {
            // 获取水平方向的速度
            Vector3 flatVel = new Vector3(rb.velocity.x, 0f, rb.velocity.z);

            // 如果水平速度的大小超过了设定的移动速度
            if (flatVel.magnitude > moveSpeed)
            {
                // 限制水平速度在设定范围内
                Vector3 limitedVel = flatVel.normalized * moveSpeed;

                // 更新刚体的速度,保持垂直速度不变
                rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z);
            }
        }
    }

    private void Jump()
    {
        exitingSlope = true;

        //rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
        rb.velocity = Vector3.zero;

        // 添加向上的力以实现跳跃
        rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
    }

    private void ResetJump()
    {
        readyToJump = true;

        exitingSlope = false;
    }

    private void StateHandler()
    {
        if (sliding)
        {
            state = MovementState.sliding;  // 设置当前状态为滑动状态

            if (OnSlope() && rb.velocity.y < 0.1f)
            {
                desiredMoveSpeed = slideSpeed;  // 如果在斜坡上并且垂直速度小于0.1,则设置期望移动速度为滑动速度
            }
            else
            {
                desiredMoveSpeed = sprintSpeed;  // 否则,设置期望移动速度为冲刺速度
            }
        }
        // 如果按住蹲伏键
        else if (Input.GetKey(crouchKey))
        {
            // 设置当前状态为蹲伏状态
            state = MovementState.crouching;
            // 设置移动速度为蹲伏速度
            desiredMoveSpeed = crouchSpeed;
        }
        // 如果在地面上并且按住冲刺键
        else if (grounded && Input.GetKey(sprintKey))
        {
            // 设置当前状态为冲刺状态
            state = MovementState.sprinting;
            // 设置移动速度为冲刺速度
            desiredMoveSpeed = sprintSpeed;
        }
        // 如果在地面上但没有按住冲刺键
        else if (grounded)
        {
            // 设置当前状态为行走状态
            state = MovementState.walking;
            // 设置移动速度为行走速度
            desiredMoveSpeed = walkSpeed;
        }
        // 如果不在地面上
        else
        {
            // 设置当前状态为空中状态
            state = MovementState.air;
        }

        if (Mathf.Abs(desiredMoveSpeed - lastDesiredMoveSpeed) > 4f && moveSpeed != 0)
        {
            StopAllCoroutines();  // 停止所有协程
            StartCoroutine(SmoothlyLerpMoveSpeed());  // 启动平滑插值移动速度的协程
        }
        else
        {
            moveSpeed = desiredMoveSpeed;  // 否则,直接将移动速度设置为期望移动速度
        }

        lastDesiredMoveSpeed = desiredMoveSpeed;  // 更新上一次的期望移动速度
    }

    public bool OnSlope()
    {
        // 使用射线检测当前位置向下,获取击中信息存储在slopeHit中
        if (Physics.Raycast(transform.position, Vector3.down, out slopeHit, playerHeight * 0.5f + 0.3f))
        {
            // 计算斜坡的角度
            float angle = Vector3.Angle(Vector3.up, slopeHit.normal);

            // 如果角度小于最大允许斜坡角度且不等于0,表示在斜坡上
            return angle < maxSlopAngle && angle != 0;
        }

        // 如果没有击中信息,或者角度不符合条件,表示不在斜坡上
        return false;

    }

    public Vector3 GetSlopeMoveDirection(Vector3 direction)
    {
        // 使用Vector3.ProjectOnPlane将移动方向投影到斜坡法线上,然后进行归一化
        return Vector3.ProjectOnPlane(direction, slopeHit.normal).normalized;
    }

    private IEnumerator SmoothlyLerpMoveSpeed()
    {
        float time = 0;  // 记录经过的时间
        float difference = Mathf.Abs(desiredMoveSpeed - moveSpeed);  // 计算期望移动速度与当前移动速度的差值
        float startValue = moveSpeed;  // 记录开始时的移动速度

        while (time < difference)
        {
            moveSpeed = Mathf.Lerp(startValue, desiredMoveSpeed, time / difference);  // 使用插值平滑地改变移动速度

            if (OnSlope())
            {
                float slopeAngle = Vector3.Angle(Vector3.up, slopeHit.normal);  // 计算当前坡度的角度
                float slopeAngleIncrease = 1 + (slopeAngle / 90f);  // 根据坡度角度增加速度

                // 根据时间、速度增加倍数、坡度增加倍数进行平滑插值
                time += Time.deltaTime * speedIncreaseMultiplier * slopeIncreaseMultiplier * slopeAngleIncrease;
            }
            else
            {
                // 在平地上,只考虑时间和速度增加倍数
                time += Time.deltaTime * speedIncreaseMultiplier;
            }

            yield return null;  // 等待下一帧
        }

        moveSpeed = desiredMoveSpeed;  // 最终将移动速度设置为期望移动速度
    }
}

新增脚本Sliding.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Sliding : MonoBehaviour
{
    public Transform orientation;  // 玩家方向的Transform
    public Transform playerObj;   // 玩家对象的Transform,用于在滑动期间调整缩放
    private Rigidbody rb;          // 玩家的刚体组件
    private PlayerMovement_03 pm_03; // PlayerMovement_03脚本的引用

    public float maxSlideTime = 0.75f; // 滑动的最大持续时间
    public float slideForce = 200;     // 滑动期间施加的力
    private float slideTimer;          // 用于跟踪滑动持续时间的计时器

    public float slideYScale = 0.5f;   // 滑动期间玩家的Y轴缩放
    private float startYScale;          // 玩家的初始Y轴缩放

    public KeyCode slideKey = KeyCode.F; // 启动滑动的按键
    private float h;  // 水平输入
    private float v;  // 垂直输入

    private void Start()
    {
        rb = GetComponent<Rigidbody>();
        pm_03 = GetComponent<PlayerMovement_03>();
        startYScale = playerObj.localScale.y;
    }

    private void Update()
    {
        h = Input.GetAxisRaw("Horizontal");
        v = Input.GetAxisRaw("Vertical");

        // 检查是否按下滑动键且存在水平或垂直输入
        if (Input.GetKeyDown(slideKey) && (h != 0 || v != 0))
        {
            StartSlide(); // 启动滑动
        }

        // 检查是否释放了滑动键且玩家当前正在滑动
        if (Input.GetKeyUp(slideKey) && pm_03.sliding)
        {
            StopSlide(); // 停止滑动
        }
    }

    private void FixedUpdate()
    {
        // 检查玩家当前是否在滑动
        if (pm_03.sliding)
        {
            SlidingMovement(); // 处理滑动运动
        }
    }

    private void StartSlide()
    {
        pm_03.sliding = true; // 在PlayerMovement_03脚本中设置滑动标志

        // 调整玩家的缩放以创建蹲伏效果
        playerObj.localScale = new Vector3(playerObj.localScale.x, slideYScale, playerObj.localScale.z);

        // 应用向下的力以模拟蹲伏
        rb.AddForce(Vector3.down * 5f, ForceMode.Impulse);

        slideTimer = maxSlideTime; // 初始化滑动计时器
    }

    private void SlidingMovement()
    {
        Vector3 inputDirection = orientation.forward * v + orientation.right * h; // 计算输入方向

        // 检查玩家是否不在斜坡上或向上移动
        if (!pm_03.OnSlope() || rb.velocity.y > -0.1f)
        {
            // 在滑动期间在输入方向上施加力
            rb.AddForce(inputDirection.normalized * slideForce, ForceMode.Force);

            slideTimer -= Time.deltaTime; // 减少滑动计时器
        }
        else
        {
            // 在斜坡上滑动时根据斜坡的方向调整力
            rb.AddForce(pm_03.GetSlopeMoveDirection(inputDirection) * slideForce, ForceMode.Force);
        }

        // 检查滑动持续时间是否已过期
        if (slideTimer <= 0)
        {
            StopSlide(); // 停止滑动
        }
    }

    private void StopSlide()
    {
        pm_03.sliding = false; // 在PlayerMovement_03脚本中重置滑动标志

        // 将玩家的缩放恢复到初始大小
        playerObj.localScale = new Vector3(playerObj.localScale.x, startYScale, playerObj.localScale.z);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第二讲_HarmonyOS应用创建和运行

HarmonyOS应用创建和运行 1. 创建一个HarmonyOS应用2. 运行新项目2.1 创建本地模拟器2.2 启动本地模拟器2.3 在本地模拟器运行项目 1. 创建一个HarmonyOS应用 打开DevEco Studio&#xff0c;在欢迎页单击Create Project&#xff0c;创建一个新工程。 选择创建Application应用。…

Vue 富文本实现内容项目倒序

应用场景&#xff1a; 比如写计划和待办事项&#xff0c;内容少还好&#xff0c;内容多了最新的内容就放在下面了&#xff0c;每次打开要滚动到最后才能看到&#xff0c;这时可以使用倒序把最新的排在最前面。 倒序前&#xff1a; 倒序后&#xff1a; 倒序代码&#xff1a; …

力扣hot100 二叉树中的最大路径和 递归

Problem: 124. 二叉树中的最大路径和 文章目录 解题方法复杂度&#x1f496; Code 解题方法 &#x1f468;‍&#x1f3eb; 参考思路 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) &#x1f496; Code /*** Definition for a binary tree no…

Vue-16、Vue列表渲染(v-for的使用)

1、vue遍历数组 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>列表渲染</title><script type"text/javascript" src"https://cdn.jsdelivr.net/npm/vue2/dist/vue.js"…

手写OpenFeign(简易版)

Remoting组件实现 1. 前言2. 原理说明3. 远程调用组件实现---自定义注解3.1 添加Spring依赖3.2 编写EnableRemoting注解3.3 编写RemoteClient注解3.4 编写GetMapping注解 4. 远程调用组件实现---生成代理类4.1 编写自定义BeanDefinition注册器4.2 编写自定义包扫描器4.3 编写Fa…

web开发学习笔记(2.js)

1.引入 2.js的两种引入方式 3.输出语句 4.全等运算符 5.定义函数 6.数组 7.数组属性 8.字符串对象的对应方法 9.自定义对象 10.json对象 11.bom属性 12.window属性 13.定时刷新时间 14.跳转网址 15.DOM文档对象模型 16.获取DOM对象&#xff0c;根据DOM对象来操作网页 如下图…

【软件测试】学习笔记-统一测试数据平台

这篇文章主要探讨全球大型电商企业中关于准备测试数据的最佳实践&#xff0c;从全球大型电商企业早期的测试数据准备实践谈起&#xff0c;分析这些测试数据准备方法在落地时遇到的问题&#xff0c;以及如何在实践中解决这些问题。其实&#xff0c;这种分析问题、解决问题的思路…

yum仓库以及NFS共享

yum实现过程 1.光驱里自带yum 2.网络下载到本地 3.直接通过网络 如何实现安装服务 yum客户端找到yum服务端&#xff0c;找到yum的仓库位置&#xff0c;下载元信息&#xff0c;因为里面有软件的位置&#xff0c;因此可以找到软件包的位置&#xff0c;然后下载到本地 仓库的类…

UI设计中插画赏析和产品色彩分析

插画赏析&#xff1a; 1. 插画是设计的原创性和艺术性的基础 无论是印刷品、品牌设计还是UI界面&#xff0c;更加风格化的插画能够将不同的风格和创意加入其中&#xff0c;在激烈的竞争中更容易因此脱颖而出。留下用户才有转化。 2. 插画是视觉触发器&#xff0c;瞬间传达大量…

OpenHarmony ArkUI ETS- 装饰器解读

前言 最近利用空闲时间在学习华为方舟开发框架&#xff08;简称&#xff1a;ArkUI&#xff09;的ets开发&#xff0c;发现在ets语言中装饰器的有着非常重要的作用&#xff0c;在写每一个自定义组件时都需要用到它&#xff0c;看到装饰器这个字眼&#xff0c;想起之前学过的设计…

模板方法模式介绍

目录 一、模板方法模式介绍 1.1 模板方法模式的定义 1.2 模板方法模式的原理 1.2.1 模板方法模式类图 1.2.2 类图角色说明 1.2.3 示例代码 二、模板方法模式的应用 2.1 需求说明 2.2 需求实现 2.2.1 账户抽象类 2.2.2 借款一个月 2.2.3 借款7天 2.2.4 测试类 三、…

kibana查看和展示es数据

本文来说下使用kibana查看和展示es数据 文章目录 数据准备查询所有文档示例kibana查看和展示es数据 数据准备 可以使用es的命令或者java程序来往&#xff0c;es进行新增数据 查询所有文档示例 在 apifox 中&#xff0c;向 ES 服务器发 GET请求 &#xff1a;http://localhost:92…

Unity之触发器

目录 &#x1f4d5;一、触发器概念 &#x1f4d5;二、碰撞与触发的区别 &#x1f4d5;三、触发器小实例 一、触发器概念 第一次玩侠盗猎车手是在小学&#xff0c;从那以后就开启了我的五星好市民之路。 下面是小编在小破站截的图&#xff0c;这是罪恶都市最开始的地方&a…

AI工具(20240116):Copilot Pro,Fitten Code等

Copilot Pro Copilot Pro是微软推出的Copilot的付费增强版本,通过提供优先访问GPT-4等最新AI模型,大大提升用户的创造力和工作效率。该服务可与Microsoft 365订阅捆绑使用,支持在Word、Excel等Office应用内直接使用Copilot功能,帮助用户更快速地起草文档、电子邮件和演示文稿等…

Kafka-消费者-KafkaConsumer分析-ConsumerCoordinator

在前面介绍了Kafka中Rebalance操作的相关方案和原理。 在KafkaConsumer中通过ConsumerCoordinator组件实现与服务端的GroupCoordinator的交互&#xff0c;ConsumerCoordinator继承了AbstractCoordinator抽象类。 下面我们先来介绍AbstractCoordinator的核心字段&#xff0c;如…

Linux下安装jdk、tomcat

linux下安装jdk、tomcat 一、linux下安装jdk1.1.下载Linux版本的JDK1.2.Linux安装JDk1.3.设置环境变量1.4.卸载JDK 二、linux下安装tomcat2.1.下载Linux版本的Tomcat2.2.在usr目录下新建tomcat目录2.3.进入到tomcat目录中解压下载的tomcat安装包2.4.配置环境变量-前提是已经安装…

HTML--CSS--盒子模型

在CSS模型中&#xff0c;所有元素都可以看做是一个盒子&#xff0c;这个盒子的组成部分&#xff1a; content 内容&#xff0c;文本或者图片 padding 内边距&#xff0c;定义内容到边框的距离 margin 外边距&#xff0c;定义当前元素与其他元素之间的距离 border 边框&#xff…

RK3566RK3568安卓11隐藏状态栏带接口

文章目录 前言一、创建全局变量二、设置应用添加隐藏导航栏按钮三、添加按钮功能四、动态隐藏还有显示功能五、创建系统导航栏广播接口总结 前言 关于Android系统的状态栏&#xff0c;不同的客户有不同的需求: 有些客户需要永久隐藏状态栏&#xff0c;有些客户需要在设置显示中…

春节假期出游一些很实用的手机技巧!这样玩,就很哇塞~

随着春节的脚步越来越近&#xff0c;无论是准备出游还是回家&#xff0c;你蠢蠢欲动的心是否已经拦不住了&#xff1f;华为 nova 12系列这些很哇塞的玩法你必须知道&#xff01;这个新年让你旅行出圈有秘籍&#xff01; 出发前智慧播报航班信息不错过。智慧播报的功能就很实…

c语言二维数组

系列文章目录 c语言二维数组 c语言二维数组 系列文章目录一、二维数组的定义一、二维数组的内存模型 一、二维数组的定义 int main() {//二维数组的定义int arr[3][4];arr[0][0]; arr[0][1]; arr[0][2]; arr[0][3]; arr[0][4];arr[1][0]; arr[1][1]; arr[1][2]; arr[1][3]; ar…