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

如何解决Character Controller可跳上陡坡的问题?

问题修复:Character Controller 陡坡滑动卡死+无法阻止跳上陡坡

问题背景

使用Unity 3D Character Controller实现角色移动时,存在两个核心问题:

  • 角色能跳上超过Controller坡度限制的陡坡
  • 按教程添加陡坡滑动逻辑后,角色滑下陡坡会卡在滑动状态,无法恢复正常移动

核心问题分析

  1. 逻辑执行顺序错误:原代码在HandleMovement之后调用滑动逻辑,导致滑动状态直接覆盖正常移动的velocity,引发状态混乱
  2. 滑动速度计算错误:SteepSlopeMovement中错误地将Time.deltaTime与速度值相加,且velocity.y的处理完全不符合物理逻辑
  3. 无状态重置逻辑:离开陡坡后未重置滑动速度,导致角色一直保持滑动状态
  4. 变量未定义:move变量未声明,会直接触发编译错误

修复方案

1. 整合陡坡判断到移动逻辑

将陡坡检测和滑动逻辑移至HandleMovement内部,优先处理陡坡状态,同时在跳跃时判断当前是否处于陡坡,阻止跳上陡坡。

2. 修复滑动方向与速度计算

基于陡坡法线正确计算向下滑动方向,用乘法处理Time.deltaTime,移除错误的velocity.y赋值,统一由重力逻辑处理垂直方向速度。

3. 添加状态重置逻辑

当离开陡坡时,重置velocity的水平分量,确保角色恢复正常移动状态。

4. 修复未定义变量

声明Vector3 move变量,解决编译错误。

修改后的完整代码

public class PlayerMovement : MonoBehaviour
{
    public CharacterController controller;

    public float speed = 12f;
    public float gravity = -9.81f;
    public float jumpHeight = 3f;

    public Transform groundCheck;
    public float groundDistance = 0.4f;
    public LayerMask groundMask;

    // SLOPES
    private float groundRayDistance = 1;
    private RaycastHit slopeHit;
    public float slopeSlideSpeed = 2f; // 建议设置合理默认值

    Vector3 velocity;
    bool isGrounded;
    bool isOnSteepSlope; // 新增:标记当前是否处于陡坡状态

    // Update is called once per frame
    void Update()
    {
        HandleMovement();
        HandleAnimations();
    }

    private void HandleMovement()
    {
        isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
        isOnSteepSlope = OnSteepSlope(); // 提前检测陡坡状态

        // 地面状态下重置垂直速度
        if (isGrounded && velocity.y < 0)
        {
            velocity.y = -2f;
        }

        // 离开陡坡时重置水平速度,解决卡死问题
        if (!isOnSteepSlope && velocity.x != 0 && velocity.z != 0)
        {
            velocity.x = 0;
            velocity.z = 0;
        }

        float x = Input.GetAxis("Horizontal");
        float z = Input.GetAxis("Vertical");
        Vector3 move = transform.right * x + transform.forward * z; // 修复:声明move变量

        // 非陡坡状态下允许正常移动和跳跃
        if (!isOnSteepSlope)
        {
            controller.Move(move * speed * Time.deltaTime);

            // 陡坡上禁止跳跃
            if (Input.GetButtonDown("Jump") && isGrounded)
            {
                velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
            }

            velocity = AdjustVelocityToSlope(velocity);
        }
        else
        {
            // 陡坡状态下执行滑动逻辑
            SteepSlopeMovement();
        }

        velocity.y += gravity * Time.deltaTime;
        controller.Move(velocity * Time.deltaTime);
    }

    private Vector3 AdjustVelocityToSlope(Vector3 velocity)
    {
        var ray = new Ray(transform.position, Vector3.down);

        if (Physics.Raycast(ray, out RaycastHit hitInfo, 0.2f))
        {
            var slopeRotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
            var adjustedVelocity = slopeRotation * velocity;

            if (adjustedVelocity.y < 0)
            {
                return adjustedVelocity;
            }
        }

        return velocity;
    }

    private bool OnSteepSlope()
    {
        if (!isGrounded) return false;

        if (Physics.Raycast(transform.position, Vector3.down, out slopeHit, (controller.height / 2) + groundRayDistance))
        {
            float slopeAngle = Vector3.Angle(slopeHit.normal, Vector3.up);
            return slopeAngle > controller.slopeLimit;
        }
        return false;
    }

    private void SteepSlopeMovement()
    {
        // 计算正确的陡坡向下滑动方向
        Vector3 slopeDirection = Vector3.Cross(Vector3.Cross(slopeHit.normal, Vector3.up), slopeHit.normal).normalized;
        // 正确计算滑动速度:基础速度+滑动加成,乘以时间增量
        float slideSpeed = (speed + slopeSlideSpeed) * Time.deltaTime;

        // 仅设置水平方向滑动速度,垂直方向由重力统一处理
        velocity.x = slopeDirection.x * slideSpeed;
        velocity.z = slopeDirection.z * slideSpeed;
    }
}

关键修改说明

  • 新增isOnSteepSlope变量追踪陡坡状态,避免重复检测
  • 调整逻辑顺序,优先处理陡坡状态,禁止陡坡上的正常移动和跳跃
  • 修复滑动方向计算,确保沿陡坡正确向下滑动
  • 移除SteepSlopeMovement中错误的velocity.y赋值,统一由重力逻辑处理垂直速度
  • 添加离开陡坡时的水平速度重置逻辑,彻底解决滑动卡死问题
  • 修复move变量未声明的编译错误

内容的提问来源于stack exchange,提问作者Carteeeer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 09:03:19