You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 06:05:01