Unity开发求助:玩家贴墙时跳跃过高的Bug修复方案
玩家贴墙时跳跃过高的Bug修复方案
我正在开发一款类《我的世界》《雷神之锤》的游戏,目前遇到跳跃机制问题:当玩家贴墙时会出现跳跃过高的异常情况。我使用Rigidbody处理物理系统,通过修改velocity实现玩家移动,玩家Collider上的Physic Material未设置摩擦和弹跳属性。以下是我的代码,寻求该Bug的修复方案或替代解决思路:
using System.Collections; using System.Collections.Generic; using UnityEngine; public enum PlayerMovementState { Sneak, Walk, Run } public class PlayerControls : MonoBehaviour { Rigidbody rb; Vector3 velocity, desiredVelocity; PlayerMovementState moveState; float groundDistance; [SerializeField] bool forwardAir, backAir, rightAir, leftAir; [SerializeField] LayerMask groundLayer; [SerializeField] bool onGround; bool desiredJump; float jumpHeight = 1.0f; private void Awake() { rb = GetComponent<Rigidbody>(); moveState = PlayerMovementState.Walk; groundDistance = GetComponentInChildren<Collider>().bounds.extents.y; } private void Update() { Vector2 playerInputs = Vector2.ClampMagnitude( new Vector2( Input.GetAxis("Horizontal"), Input.GetAxis("Vertical") ), 1.0f ); if (Input.GetKey(KeyCode.LeftShift)) moveState = PlayerMovementState.Sneak; else if (Input.GetKey(KeyCode.LeftControl)) moveState = PlayerMovementState.Run; else moveState = PlayerMovementState.Walk; float speed = moveState == PlayerMovementState.Run ? 10f : ( moveState == PlayerMovementState.Sneak ? 2f : 5f ); RaycastGround(); onGround = !forwardAir && !backAir && !rightAir && !leftAir; if (Input.GetButtonDown("Jump")) desiredJump = true; if (moveState == PlayerMovementState.Sneak) { if (forwardAir && playerInputs.y > 0) playerInputs.y = 0f; if (backAir && playerInputs.y < 0) playerInputs.y = 0f; if (rightAir && playerInputs.x > 0) playerInputs.x = 0f; if (leftAir && playerInputs.x < 0) playerInputs.x = 0f; } desiredVelocity = (transform.forward * playerInputs.y + transform.right * playerInputs.x) * speed; } private void FixedUpdate() { velocity = rb.velocity; float acceleration = 10; velocity.x = Mathf.MoveTowards(velocity.x, desiredVelocity.x, acceleration); velocity.z = Mathf.MoveTowards(velocity.z, desiredVelocity.z, acceleration); if (desiredJump && onGround) { desiredJump = false; float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight); velocity.y += jumpSpeed; } rb.velocity = velocity; desiredJump = false; } void RaycastGround() { forwardAir = !(Physics.Raycast( transform.position + Vector3.forward * 0.1f, -Vector3.up, groundDistance + 0.1f, groundLayer )); backAir = !(Physics.Raycast( transform.position - Vector3.forward * 0.1f, -Vector3.up, groundDistance + 0.1f, groundLayer )); rightAir = !(Physics.Raycast( transform.position + Vector3.right * 0.1f, -Vector3.up, groundDistance + 0.1f, groundLayer )); leftAir = !(Physics.Raycast( transform.position - Vector3.right * 0.1f, -Vector3.up, groundDistance + 0.1f, groundLayer )); } }
问题原因分析
- 地面检测逻辑缺陷:当前
onGround要求四个方向的射线全部触地才判定为在地面,既不符合实际需求(玩家只要脚部接触地面就应允许跳跃),还可能在贴墙时出现误判;更关键的是,贴墙时的碰撞可能导致Rigidbody的y轴残留微小向上速度。 - 跳跃速度叠加错误:代码中使用
velocity.y += jumpSpeed而非直接赋值,若此时y轴已有向上速度,叠加后总跳跃速度会远高于预期,导致跳得过高。
修复方案
1. 替换可靠的地面检测
将原有的四条射线检测改为Physics.CheckSphere,检测玩家脚下的球形区域是否接触地面层,大幅提升检测准确性:
void CheckGround() { // 检测玩家脚下略低于碰撞体底部的球形区域 Vector3 checkPos = transform.position - Vector3.up * (groundDistance - 0.05f); float checkRadius = GetComponentInChildren<Collider>().bounds.extents.x * 0.8f; onGround = Physics.CheckSphere(checkPos, checkRadius, groundLayer); }
然后在Update中替换原有的RaycastGround()调用为CheckGround(),同时删除forwardAir、backAir等冗余变量及相关逻辑。
2. 修改跳跃速度赋值方式
将跳跃时的速度叠加改为直接赋值,避免残留速度影响:
if (desiredJump && onGround) { desiredJump = false; float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight); // 直接设置y轴速度为跳跃速度,而非叠加 velocity.y = jumpSpeed; }
3. 优化跳跃输入逻辑
移除FixedUpdate末尾的desiredJump = false,避免刚触发的跳跃输入被直接清空:
private void FixedUpdate() { velocity = rb.velocity; float acceleration = 10; velocity.x = Mathf.MoveTowards(velocity.x, desiredVelocity.x, acceleration); velocity.z = Mathf.MoveTowards(velocity.z, desiredVelocity.z, acceleration); if (desiredJump && onGround) { desiredJump = false; float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight); velocity.y = jumpSpeed; } rb.velocity = velocity; // 移除这行代码,保留未处理的跳跃输入 // desiredJump = false; }
替代思路(可选)
如果仍想保留射线检测,可改为只要有一条射线触地就判定为在地面,修改onGround判断逻辑:
onGround = !forwardAir || !backAir || !rightAir || !leftAir;
但这种方式仍不如球形检测可靠,建议优先使用前者覆盖玩家脚部的整个接触区域,减少误判。
内容的提问来源于stack exchange,提问作者mikeriin
相关产品推荐
相关产品推荐

