20240116-【UNITY 学习】增加滑动功能
新增滑动功能,在移动时按下F键可进行一定时间的滑动,当处于下坡状态时,会一直处于滑动状态。
·
替换脚本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);
}
}
更多推荐
所有评论(0)