Unity中Capsule角色控制器水平移动卡边缘问题求助
Capsule角色控制器沿墙斜移卡顿修复方案
核心问题分析
斜向移动时卡顿、陷墙,本质是单次大位移触发多边缘碰撞,导致CharacterController的碰撞修正逻辑冲突;仅单法线投影无法处理墙角等多碰撞面场景。
可行解决方案
1. 分步小位移+多碰撞面检测
将单次移动拆分为多步小位移,每步检测碰撞并基于多个碰撞法线修正方向,避免边缘卡滞:
using UnityEngine; public class WallMoveFix : MonoBehaviour { public float moveSpeed = 5f; public float stepSize = 0.05f; // 越小越流畅,性能消耗略增 private CharacterController _controller; void Start() { _controller = GetComponent<CharacterController>(); // 调整控制器参数优化基础表现 _controller.skinWidth = 0.08f; _controller.minMoveDistance = 0.001f; } void Update() { Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")).normalized; Vector3 moveDir = transform.TransformDirection(input); moveDir.y = 0; // 平面移动忽略Y轴,可根据需求调整 float remainingDist = moveSpeed * Time.deltaTime; while (remainingDist > Mathf.Epsilon) { float currentStep = Mathf.Min(remainingDist, stepSize); Vector3 tempMove = moveDir * currentStep; // 执行小步移动 _controller.Move(tempMove); // 检测碰撞并修正方向 if (_controller.collisionFlags != CollisionFlags.None) { Vector3 averageNormal = Vector3.zero; int hitCount = 0; // 检测胶囊体上下半球+中心的碰撞 Vector3[] checkPoints = new Vector3[] { transform.position, transform.position + Vector3.up * (_controller.height / 2 - _controller.radius), transform.position - Vector3.up * (_controller.height / 2 - _controller.radius) }; foreach (var point in checkPoints) { if (Physics.SphereCast(point, _controller.radius, tempMove.normalized, out RaycastHit hit, currentStep)) { averageNormal += hit.normal; hitCount++; } } if (hitCount > 0) { averageNormal = averageNormal.normalized; // 投影到碰撞法线的垂直平面,保留沿墙移动方向 moveDir = Vector3.ProjectOnPlane(moveDir, averageNormal).normalized; } } remainingDist -= currentStep; } } }
2. 预检测胶囊体路径(简化版)
移动前用Physics.CapsuleCast预判碰撞,提前修正方向,适合性能敏感场景:
void Update() { Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")).normalized; Vector3 moveDir = transform.TransformDirection(input); moveDir.y = 0; float totalDist = moveSpeed * Time.deltaTime; // 定义胶囊体的上下端点 Vector3 capsuleBottom = transform.position - Vector3.up * _controller.height / 2 + Vector3.up * _controller.radius; Vector3 capsuleTop = transform.position + Vector3.up * _controller.height / 2 - Vector3.up * _controller.radius; if (Physics.CapsuleCast(capsuleBottom, capsuleTop, _controller.radius, moveDir, out RaycastHit hit, totalDist)) { // 先移动到碰撞点前的位置 _controller.Move(moveDir * hit.distance); // 剩余距离沿修正方向移动 float remaining = totalDist - hit.distance; if (remaining > 0) { Vector3 correctedDir = Vector3.ProjectOnPlane(moveDir, hit.normal).normalized; _controller.Move(correctedDir * remaining); } } else { _controller.Move(moveDir * totalDist); } }
3. 控制器参数优化
- Skin Width:设为0.08~0.1,给碰撞检测留缓冲,避免边缘硬卡
- Min Move Distance:设为0.001以下,确保小位移不会被忽略
- Radius/Height:避免胶囊体半径刚好等于墙壁棱角的宽度,适当调整尺寸
内容的提问来源于stack exchange,提问作者Niek Kleiboer
相关产品推荐
相关产品推荐

