如何解决Character Controller可跳上陡坡的问题?
问题修复:Character Controller 陡坡滑动卡死+无法阻止跳上陡坡
问题背景
使用Unity 3D Character Controller实现角色移动时,存在两个核心问题:
- 角色能跳上超过Controller坡度限制的陡坡
- 按教程添加陡坡滑动逻辑后,角色滑下陡坡会卡在滑动状态,无法恢复正常移动
核心问题分析
- 逻辑执行顺序错误:原代码在
HandleMovement之后调用滑动逻辑,导致滑动状态直接覆盖正常移动的velocity,引发状态混乱 - 滑动速度计算错误:
SteepSlopeMovement中错误地将Time.deltaTime与速度值相加,且velocity.y的处理完全不符合物理逻辑 - 无状态重置逻辑:离开陡坡后未重置滑动速度,导致角色一直保持滑动状态
- 变量未定义:
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
相关产品推荐
相关产品推荐

