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 物理角色控制,这个方案非常值得直接落地。

相关推荐
不绝19113 小时前
导航系统/NavMeshAgent组件
unity
mxwin15 小时前
Unity Shader 屏幕空间 UVScreen Space UV 完全指南
unity·游戏引擎·uv
LF男男18 小时前
TouchManager
unity·c#
mxwin19 小时前
Unity Shader 径向模糊与径向 UV 变形速度感 · 冲击波效果完全指南
unity·游戏引擎·shader·uv
weixin_4239950020 小时前
unity 微信开发小游戏,网络资源获取数据
unity·游戏引擎
Yasin Chen20 小时前
Unity TMP_SDF 分析(五)片元着色器
unity·游戏引擎·着色器
mxwin21 小时前
Unity Shader Texture Bombing用随机旋转与偏移的多次采样,打破大地形纹理的
unity·游戏引擎
代数狂人21 小时前
《深入浅出Godot 4与C# 3D游戏开发》第二章:编辑器导航
3d·编辑器·游戏引擎·godot
zcc85807976221 小时前
Unity MVVM UniTask + 轻量级 ReactiveProperty
unity