Unity斜坡移动脚本故障:角色穿透Ground层斜坡问题
问题场景
已将场景内所有斜坡的Layer设置为Ground,同时把斜坡检测器的目标检测Layer也设为Ground,预期触发斜坡专属移动逻辑,让角色正常上下斜坡。但使用《Rigidbody FPS Controller Tutorial #3 | Slope Handling + FIXES!》中的PlayerMovement脚本后,角色直接穿透斜坡掉落,无法定位问题。
使用的代码如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerMovement : MonoBehaviour { float playerHeight = 2f; [SerializeField] Transform orientation; [Header("Movement")] public float moveSpeed = 6f; public float movementMultiplier = 10f; [SerializeField] float airMultiplier = 0.4f; [Header("Keybinds")] [SerializeField] KeyCode jumpKey = KeyCode.Space; [Header("Jumping")] public float jumpForce = 5f; [Header("Drag")] float groundDrag = 6f; float airDrag = 2f; float rbDrag = 6f; float horizontalMovement; float verticalMovement; [Header("Ground Detection")] [SerializeField] LayerMask groundMask; bool isGrounded; float groundDistance = 0.4f; Vector3 moveDirection; Vector3 slopeMoveDirection; Rigidbody rb; RaycastHit slopeHit; private bool OnSlope() { if (Physics.Raycast(transform.position, Vector3.down, out slopeHit, playerHeight /2 + 0.5f)) { if (slopeHit.normal != Vector3.up) { return true; } else { return false; } } return false; } private void Start() { rb = GetComponent<Rigidbody>(); rb.freezeRotation = true; } private void Update() { isGrounded = Physics.CheckSphere(transform.position - new Vector3(0, 1, 0), groundDistance, groundMask); MyInput(); ControlDrag(); if (Input.GetKeyDown(jumpKey) && isGrounded) { Jump(); } slopeMoveDirection = Vector3.ProjectOnPlane(moveDirection, slopeHit.normal); } void MyInput() { horizontalMovement = Input.GetAxisRaw("Horizontal"); verticalMovement = Input.GetAxisRaw("Vertical"); moveDirection = orientation.forward * verticalMovement + orientation.right * horizontalMovement; } void Jump() { rb.AddForce(transform.up * jumpForce, ForceMode.Impulse); } void ControlDrag() { if (isGrounded) { rb.drag = groundDrag; } else { rb.drag = airDrag; } } private void FixedUpdate() { MovePlayer(); } void MovePlayer() { if (isGrounded && !OnSlope()) { rb.AddForce(moveDirection.normalized * moveSpeed * movementMultiplier, ForceMode.Acceleration); } else if (isGrounded && OnSlope()) { rb.AddForce(slopeMoveDirection.normalized * moveSpeed * movementMultiplier, ForceMode.Acceleration); } else if (!isGrounded) { rb.AddForce(moveDirection.normalized * moveSpeed * movementMultiplier * airMultiplier, ForceMode.Acceleration); } } }
排查与修复方案
1. 地面检测Sphere位置偏移问题
代码中isGrounded的检测Sphere位置是transform.position - new Vector3(0,1,0),若角色碰撞体中心与transform.position不重合(比如胶囊体碰撞体中心偏移),会导致检测点未触碰到斜坡,isGrounded误判为false,角色进入空中移动逻辑进而穿透斜坡。
- 修复:将检测位置改为角色碰撞体底部,比如:
isGrounded = Physics.CheckSphere(transform.position - new Vector3(0, playerHeight/2, 0), groundDistance, groundMask);
2. 斜坡检测Raycast长度不足
OnSlope()中Raycast长度为playerHeight/2 +0.5f(1.5f),若斜坡碰撞体位置较低或角色碰撞体较大,可能无法检测到斜坡,导致OnSlope()返回false,走平地移动逻辑穿透斜坡。
- 修复:调整Raycast长度为
playerHeight/2 + groundDistance,确保覆盖地面检测范围:if (Physics.Raycast(transform.position, Vector3.down, out slopeHit, playerHeight /2 + groundDistance))
3. 斜坡移动方向计算时机错误
当前slopeMoveDirection在Update()中计算,但OnSlope()未提前更新slopeHit数据,可能使用旧的法线计算方向;且MovePlayer()中重复调用OnSlope()会导致数据不一致。
- 修复:在
Update()中先更新斜坡检测数据,再计算移动方向,避免重复Raycast:// 类内新增变量 bool onSlope; private void Update() { isGrounded = Physics.CheckSphere(transform.position - new Vector3(0, playerHeight/2, 0), groundDistance, groundMask); onSlope = OnSlope(); // 先更新slopeHit数据 MyInput(); ControlDrag(); if (Input.GetKeyDown(jumpKey) && isGrounded) { Jump(); } slopeMoveDirection = Vector3.ProjectOnPlane(moveDirection, slopeHit.normal); } void MovePlayer() { if (isGrounded && !onSlope) { rb.AddForce(moveDirection.normalized * moveSpeed * movementMultiplier, ForceMode.Acceleration); } else if (isGrounded && onSlope) { rb.AddForce(slopeMoveDirection.normalized * moveSpeed * movementMultiplier, ForceMode.Acceleration); } else if (!isGrounded) { rb.AddForce(moveDirection.normalized * moveSpeed * movementMultiplier * airMultiplier, ForceMode.Acceleration); } }
4. Rigidbody碰撞设置问题
检查角色Rigidbody组件:
- 将
Collision Detection设为Continuous或Continuous Dynamic,避免高速移动穿透; - 确认
Constraints中三个旋转轴均勾选冻结(代码已设置,但需检查组件面板); - 确保斜坡碰撞体未开启
Is Trigger,否则会导致碰撞失效。
5. LayerMask赋值错误
检查Inspector面板中groundMask是否仅勾选Ground层,若误选其他层或未勾选Ground,会导致地面检测失效。
内容的提问来源于stack exchange,提问作者Big Glasses
相关产品推荐
相关产品推荐

