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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 09:15:12