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

相关推荐
正在走向自律19 分钟前
anywhere-labs/deepseek-harness-desktop 双模式界面设计:兼容性、原生材质与 Slot 组合
材质·deepseek·dshdesktop·web应用桌面·双模式方案·web产品
电子云与长程纠缠1 小时前
UE5 Lyra PocketWorld进行3D内容UI预览 - 上
开发语言·学习·3d·ue5·游戏引擎
玖玥拾1 小时前
Unity3D RPG 入门项目(八)游戏设置面板、帧率控制、快捷技能药品栏、技能解锁系统
游戏·3d·unity·游戏引擎
ellis19702 小时前
u3d插件xLua[十] lua侧判空问题
unity
牛哇网络工作室12 小时前
UnityHDRP写实数字人全流程基础5—语音输入和语音识别
android·unity·c#·游戏引擎·aigc·语音识别·xcode
ellis19701 天前
u3d插件xLua[九]例8 Hotfix
unity
淡海水1 天前
01-07-运行时-GC深度剖析-内存分配回收与结构选择
jvm·windows·unity·gc
优梦创客1 天前
游戏开发架构选型:第1篇|什么是架构?从进球事件看 MVC 的局限
unity·架构·游戏引擎·游戏开发
tianxuanjg2 天前
0.005mm 高精度 CNC 加工:机器人薄壁复杂零件变形控制与全维度品控方案
经验分享·机器人·无人机·制造·材质
tianxuanjg2 天前
机器人精密零件:为何样品合格,批量却频频超差?
人工智能·经验分享·机器人·无人机·制造·材质