Unity 角色“防卡墙”实战:不用动态物理材质,也能稳定解决 Wedging 问题

在很多基于 Rigidbody 的角色项目里,大家遇到"卡墙"时第一反应是改摩擦系数、切换物理材质,甚至运行时动态改材质。

我们这次走了另一条路:不依赖动态物理材质,而是用"预测胶囊 + ComputePenetration 挤出向量"做防卡墙,最终稳定解决了问题。

这篇文章记录完整思路和落地方式。


1. 问题背景

项目中的角色移动使用 AddForce 驱动,场景有大量复杂几何(边角、凹凸 Mesh、环形结构)。

常见异常:

  1. 明明有移动输入,但角色贴墙后"推不动"。
  2. 某些边角处速度突然被吃掉。
  3. 偶发楔入(wedging),表现为抖动、短距离抽搐或原地卡住。

2. 为什么不再依赖"动态物理材质"方案

很多项目会尝试在代码里动态切换 friction/bounce 来缓解卡墙,但这种方式有几个硬伤:

  1. 治标不治本

    卡墙核心是接触几何与解算方向问题,不是单纯摩擦问题。

  2. 全局副作用大

    改材质会影响整套接触行为(滑移、落地、平台交互、推挤手感)。

  3. 调参成本高、稳定性差

    一处场景调好了,另一处复杂结构可能又失效。

  4. 行为不一致

    不同 FPS、不同接触体组合时,动态材质策略往往表现漂移。

我们最后选择:玩家和子物体都使用默认材质,不在运行时改材质参数 。

把防卡墙交给"几何预测 + 挤出向量"来处理,结果更稳。


3. 核心方案:Predictive Capsule + Penetration Push

最终方案分 5 步:

  1. 预测下一物理步角色胶囊位置(仅水平分量)。
  2. 在预测位姿做 OverlapCapsuleNonAlloc。
  3. 对每个重叠体调用 Physics.ComputePenetration。
  4. 聚合所有 separationDir * separationDistance 得到总挤出向量。
  5. 只处理水平向内分量:
    • 去掉"继续往里顶"的加速度
    • 可选抵消当前"向里"的水平速度(不加反弹)

重点:不改 Y 轴,不覆盖跳跃系统,不依赖动态材质。


4. 为什么它比单次 SweepTest 稳

单 SweepTest 适合"前方是否有障碍"的预警,但对 wedging 场景信息不够。

而 ComputePenetration 给的是"最小分离向量",可以直接回答:

  • 现在应该往哪边解开重叠?
  • 多个接触体同时挤压时,合力方向是什么?

这正是防卡墙真正需要的信息。


5. 精简代码思路

cs 复制代码
// 预测水平位移
Vector3 predictedDelta = horizontalV * dt + accPlanar * (dt * dt);
predictedDelta += desiredDir.normalized * movementSweepPadding;

// 预测位姿胶囊重叠检测
int hitCount = Physics.OverlapCapsuleNonAlloc(
    pointA, pointB, radius, overlapBuffer, movementSweepBlockMask, QueryTriggerInteraction.Ignore);

// 聚合挤出向量
Vector3 planarPush = Vector3.zero;
for (int i = 0; i < hitCount; i++)
{
    Collider other = overlapBuffer[i];
    if (other == null || other.isTrigger) continue;

    if (Physics.ComputePenetration(
        capsule, predictedPos, predictedRot,
        other, other.transform.position, other.transform.rotation,
        out Vector3 sepDir, out float sepDist))
    {
        Vector3 planarDir = Vector3.ProjectOnPlane(sepDir, Vector3.up);
        if (planarDir.sqrMagnitude > 1e-6f && sepDist > 0f)
            planarPush += planarDir.normalized * sepDist;
    }
}

// 只移除向内推进,不改Y
if (planarPush.sqrMagnitude > 1e-6f)
{
    Vector3 outward = planarPush.normalized;
    Vector3 intoObstacle = -outward;

    float intoAcc = Vector3.Dot(accelerationStep, intoObstacle);
    if (intoAcc > 0f) accelerationStep -= intoObstacle * intoAcc;
}

6. 参数建议

  1. movementSweepPadding: 0.05 ~ 0.12
  2. movementSweepMaxCancelAcceleration: 25 ~ 60
  3. movementSweepBlockMask: 只包含会阻挡移动的层

7. 这套"防卡墙"方案的价值

  1. 不依赖动态物理材质,避免全局副作用。
  2. 对复杂场景更稳,特别是边角/环形/凹凸结构。
  3. 不破坏原有跳跃与竖直力模型。
  4. 参数可控、行为一致性更高。

结论

卡墙问题的核心是"接触解算方向"而不是"摩擦值本身"。

把策略从"动态改材质"切换到"预测胶囊 + 挤出向量",防卡墙会从"偶发有效"变成"稳定可控"。

如果你也在做 Unity 物理角色控制,这个方案非常值得直接落地。

相关推荐
警醒与鞭策2 小时前
【无标题】
android·unity·性能优化·游戏引擎·perforce
zhchyun20083 小时前
【Unity UI 进阶】仿 Element UI 打造企业级 Unity UI 组件库(10)
ui·unity·游戏引擎
地狱为王8 小时前
Unity使用NVIDIA的Audio2Face驱动口型
unity·游戏引擎
ebiobiz10 小时前
STM32F4 Unity 单元测试与多次测试失败率统计详细步骤
stm32·unity·单元测试
一线灵15 小时前
跨平台游戏引擎 Axmol-2.11.5 发布
游戏引擎·lua
淡海水15 小时前
16-01-C#数据结构源码-附录A-术语表
unity·c#·游戏引擎·il2cpp
淡海水16 小时前
16-03-C#数据结构源码-附录C-参考资源
unity·c#·游戏引擎·il2cpp
天神哥哥啊17 小时前
unity联调注意事项-安卓
android·unity·游戏引擎
天神哥哥啊17 小时前
unity联调注意事项-IOS
unity·ios·游戏引擎
Jmyd01231 天前
手持激光三维扫描仪技术解析:三角测量数据链路、材质适配矩阵与点云处理流程
数码相机·矩阵·材质·三维数字化