ESP速度投影:拒绝方向投影的碰撞检测新思路与工程实践

发布时间:2026/11/3 22:24:08
ESP速度投影:拒绝方向投影的碰撞检测新思路与工程实践

1. 从一次"方向投影翻车"说起:ESP到底想解决什么

如果你做过任何跟物理模拟、碰撞检测或者角色移动相关的东西,大概率踩过这样一个坑:物体沿着某个方向高速移动,你按这个方向做投影或者扫掠检测,结果它偏偏从两个碰撞体之间的缝隙里"穿"过去了,或者贴着一个斜面滑行时突然被弹飞。这类问题的根源,往往不在检测算法本身,而在于投影方向的选择——你选了一个"看起来合理"的方向,但那个方向恰好是几何上最脆弱的方向。

Strata 里的 ESP,全称是 Experimental Speed Projection,直译过来就是"实验性速度投影"。名字里带"实验性"三个字,本身就说明它不是那种已经被打磨到无懈可击的成熟方案,而是一个在特定场景下能带来明显收益、但同时需要你清楚它边界在哪的取舍。它的核心主张非常直接:拒绝方向投影。也就是说,不再沿着速度方向或者某个预设的扫描方向去做投影求解,而是换一套思路来处理速度与几何的关系。

我第一次接触这个概念的时候,直觉反应是"不沿方向投影,那怎么判断会不会撞上"。这其实是大多数人的第一反应,因为我们习惯了"往哪走就往哪看"的线性思维。但 ESP 的价值恰恰在于打破这个惯性:方向投影在某些几何配置下会系统性地产生错误结果,而错误不是随机的,是有规律的、可预测的,这就意味着它可以被针对性地规避。

这篇内容适合两类人看。一类是正在做移动、碰撞、扫掠相关模块,已经被"穿模""卡角""贴面抖动"折磨过的开发者;另一类是听说过 ESP 但没搞明白它到底在拒绝什么、收益从哪来、风险又在哪的人。我会把原理拆开讲清楚,把收益用具体场景说明白,更重要的是把安全风险摊开——因为"实验性"这三个字不是装饰,它意味着你在享受收益的同时,必须自己兜住那些它没帮你兜住的东西。

需要先说明一点:Strata 这个项目本身的公开细节有限,下面涉及的具体实现思路,一部分来自标题和概念的合理推演,一部分来自同类速度投影方案的常见实践。我会在关键处标注哪些是基于常见工程实践的补充,避免让你误以为那是项目原文。

2. 方向投影为什么会"骗"你:几何上的系统性偏差

2.1 方向投影的隐含假设

要理解 ESP 为什么拒绝方向投影,得先搞清楚方向投影到底在假设什么。当你沿着速度方向做投影时,本质上你做了一个很强的假设:物体在下一时刻的位置,一定落在这条射线上。这个假设在匀速直线运动、且几何体都是凸的、且没有其他约束介入的情况下是成立的。但现实里的运动几乎从不满足这三个条件。

举个最典型的例子。一个角色沿着地面斜向奔跑,速度方向与地面成一个小角度。方向投影会沿着这条斜线去找碰撞点,找到之后把速度分解成沿面和垂直面两个分量,垂直分量消掉,沿面分量保留。听起来没问题,对吧?但当角色同时贴着两面墙形成的内角移动时,沿速度方向的射线可能先打到墙 A,投影完之后新的速度方向又指向墙 B,于是下一帧再投影一次。两次投影之间,角色实际上被"推"进了一个几何上并不存在的位置,表现出来就是卡角、抖动,甚至被弹到墙外。

这就是方向投影的第一个系统性偏差:它把多维约束问题降成了一维射线问题。射线只能告诉你"这个方向上第一个碰到的是什么",但没法同时告诉你"所有方向上哪些位置是合法的"。当多个约束同时生效时,逐次投影的结果依赖于投影顺序,而顺序本身是没有几何意义的。

2.2 薄几何与高速运动下的穿透

第二个偏差更隐蔽,也更致命。方向投影的检测精度依赖于步长或者扫掠的离散化程度。当物体速度很快、而碰撞体很薄时,射线可能从薄壁的一侧出发,在离散采样点之间"跳过"了薄壁,直接到达另一侧。这就是经典的穿透问题。

很多人第一反应是"那就把步长调小"。但步长调小带来的是计算量线性上升,而且它只是把穿透概率降低,并没有从原理上消除。更麻烦的是,步长小到一定程度后,浮点精度开始介入,你会在薄壁附近看到各种诡异的抖动和误判。方向投影在这个问题上是被动的:它的检测能力受限于采样密度,而采样密度又受限于性能预算。

ESP 拒绝方向投影,某种程度上就是在拒绝这种"靠加密采样来兜底"的思路。它试图换一个不依赖单一方向采样的框架,让薄几何和高速运动不再成为系统性弱点。具体怎么换,下一节展开。

2.3 一个容易被忽略的细节:投影方向与法线的关系

还有一个坑,只有真正调过参数的人才会注意到。方向投影在求解时,经常需要用到碰撞点的法线来做速度分解。但法线的计算本身依赖于你找到的那个碰撞点,而碰撞点又依赖于投影方向。这就形成了一个微妙的循环依赖:方向选得不好,找到的碰撞点可能落在几何的棱上或者退化面上,那里法线是不连续甚至无定义的,分解出来的结果自然就是错的。

我见过不少案例,表面上是"贴面滑行时突然加速"或者"沿墙走时被吸进去",追到根子上都是棱边法线的问题。方向投影对此几乎没有防御能力,因为它没有机制去判断"我找到的这个点是不是一个可靠的分解基准"。ESP 在设计上对这类退化情况有额外的考量,这也是它收益的一部分来源。

3. ESP 的拒绝策略:不沿方向走,那沿什么走

3.1 从"射线"到"可行域"的思路转换

ESP 最核心的思路转换,是把问题从"沿某个方向找第一个碰撞"变成"求出当前时刻所有合法位置的集合,再看速度能把物体带到这个集合里的哪个位置"。前者是射线思维,后者是可行域思维。

打个比方。方向投影像是你蒙着眼睛往前走,用手杖往前戳,戳到东西就停。可行域思维则是你先睁开眼睛看清周围哪些地方是空的,然后判断往前走一步会不会离开空地。前者快但容易漏,后者慢但不容易错。ESP 的"实验性"很大程度上就体现在这个"慢"上——它用更多的几何查询换取了更稳的结果。

具体到实现层面,可行域思维通常表现为:对物体周围的约束做一次聚合查询,得到一个局部的合法空间描述,然后把速度投影到这个空间的边界上。这个描述可以是多个半空间的交集,也可以是一组接触约束的集合。关键在于,它是同时考虑所有约束的,而不是逐个投影。

3.2 速度投影与位置修正的解耦

ESP 另一个值得说的设计取向,是把速度投影和位置修正分开处理。方向投影方案里,这两件事经常是耦合的:投影完速度,位置顺带就更新了,一旦投影出错,位置也跟着错,而且错误会累积到下一帧。

解耦之后,速度投影只负责回答"这一帧物体应该以什么速度运动",位置修正则单独回答"如果当前位置已经非法了,怎么把它拉回来"。这样做的好处是,速度层面的错误不会直接污染位置,位置层面的修正也不会反过来扭曲速度。对于高速运动场景,这个解耦尤其重要,因为高速下速度方向的微小误差会被放大成很大的位置偏差,耦合方案很容易雪崩。

提示:解耦不是免费的。它意味着你需要维护两套逻辑,并且在两者不一致时决定谁优先。这个决策点本身就是 ESP 风险的一部分,后面会专门讲。

3.3 为什么叫"速度投影"而不是"位置投影"

名字里的"速度投影"是有讲究的。位置投影是直接把物体从一个非法位置挪到合法位置,速度投影则是调整速度向量,让物体在下一时刻自然到达合法位置。前者是"事后补救",后者是"事前引导"。

事前引导的好处是不会产生"瞬移感"。位置投影在修正较大偏差时,物体看起来像是被硬拽了一下;速度投影则是让物体平滑地改变运动方向,视觉上更自然。但代价是,速度投影对当前状态的合法性有要求——如果物体已经处在非法位置,光调速度是救不回来的,还是得靠位置修正兜底。所以 ESP 里这两者是配合关系,不是替代关系。

4. 收益盘点:哪些场景下 ESP 明显更稳

4.1 内角与多约束并发的场景

内角场景是方向投影的重灾区,也是 ESP 收益最明显的地方。两面墙形成的 90 度内角,角色斜着走进去,方向投影会先撞一面墙、再撞另一面,两次投影之间产生的位置漂移会让角色卡在角里或者被弹出来。ESP 因为同时考虑两面墙的约束,能直接算出"沿角平分线方向滑出"的合法速度,角色走进去是顺滑地拐弯,而不是卡顿。

这个差异在小角度内角(比如 30 度、45 度)下更夸张。方向投影在这种角里几乎必然抖动,因为两次投影的方向差异很大,速度被反复折返。ESP 的可行域描述天然处理这种窄角,只要可行域非空,就能给出稳定解。

4.2 薄壁与高速穿透

前面说过,方向投影靠加密采样来对抗穿透,ESP 则试图从约束聚合的角度绕开这个问题。当薄壁作为约束之一被纳入可行域计算时,物体是否穿透取决于可行域是否被正确切分,而不是取决于采样点是否恰好落在壁上。这在高速运动下优势明显:即使一帧内位移很大,只要可行域计算正确,物体也不会跳过薄壁。

不过这里要泼一盆冷水。ESP 在薄壁场景的收益,高度依赖于约束聚合的精度。如果聚合过程本身做了近似或者裁剪,薄壁可能被"聚合掉",穿透照样发生。所以这个收益不是无条件的,它把压力从"采样密度"转移到了"聚合精度"上。这一点在选型时必须想清楚。

4.3 贴面滑行的稳定性

贴面滑行是另一个受益场景。方向投影在贴面时,每一帧都要重新找碰撞点、重新算法线、重新分解速度,任何一步的数值误差都会累积成滑行方向的漂移。ESP 因为速度投影是基于约束集合的,滑行方向由约束的几何关系直接决定,误差不会逐帧累积。

我用一个类比来解释这个差异。方向投影像是每走一步都重新问路,问路的人每次可能给你指偏一点点,走多了就偏到沟里。ESP 像是先看地图再走,地图可能有误差,但误差是固定的,不会因为你走了多少步而放大。

4.4 收益的边界:不是所有场景都值得上 ESP

必须说清楚,ESP 不是万能药。在简单场景——单个凸碰撞体、低速运动、没有多约束并发——方向投影又快又准,上 ESP 纯属杀鸡用牛刀,还平白引入复杂度和风险。ESP 的收益曲线是随场景复杂度上升的:约束越多、速度越快、几何越刁钻,它的相对优势越明显;反过来,简单场景下它的开销和风险都是净损失。

所以选型的第一原则是先判断你的场景复杂度分布。如果你的项目里 90% 的碰撞是简单凸体低速接触,只有 10% 是复杂场景,那更务实的做法可能是保留方向投影做主干,只在复杂场景分支里启用 ESP,而不是全局替换。

5. 安全风险:实验性三个字背后的代价

5.1 可行域计算的数值脆弱性

ESP 的收益建立在可行域计算上,而可行域计算的数值稳定性是个大问题。多个半空间求交,在接近退化的情况下(比如两个约束几乎平行、或者交线非常接近物体当前位置),浮点误差会被急剧放大。方向投影虽然也有数值问题,但它的计算链条短,误差传播路径清晰;ESP 的计算链条长,误差可能在聚合、求交、投影多个环节里累积,最后的结果可能偏离正确值很远,而且很难定位是哪一步出的问题。

实际表现就是:大部分时候很稳,偶尔在特定几何配置下出现完全无法解释的异常。这种"偶发且难复现"的 bug 是最消耗调试时间的,也是 ESP 被标为"实验性"的主要原因之一。

5.2 解耦带来的状态不一致

前面提到速度投影和位置修正解耦是 ESP 的设计取向,但解耦本身带来一个风险:两者可能给出矛盾的结论。比如速度投影认为"当前速度合法,可以继续走",但位置修正发现"当前位置已经非法,需要拉回"。这时候听谁的?

如果听速度的,非法位置会持续,误差累积;如果听位置的,速度被强行改变,可能出现视觉上的顿挫。更麻烦的是,这个矛盾可能在帧与帧之间反复横跳,表现为物体在原地高频抖动。方向投影因为耦合在一起,反而不会有这种"两个大脑打架"的问题。ESP 需要你显式地设计一套仲裁逻辑,而这套逻辑的正确性又依赖于你对场景的理解,没有通用解。

5.3 性能开销的不确定性

可行域计算的开销不是常数,它随约束数量增长,而且在约束密集的区域会显著上升。方向投影的开销相对可预测——一次射线查询,成本基本固定。ESP 的开销在平均情况下可能可以接受,但在最坏情况下(比如物体被大量约束包围)可能突然飙升,造成帧率尖峰。

这个特性对实时应用很不友好,因为帧率尖峰比平均帧率低更影响体验。缓解办法通常是给可行域计算设一个约束数量上限,超过就退化到方向投影。但这又引入了新的问题:退化路径和主路径的行为不一致,切换时可能出现视觉跳变。这个坑我在实际项目里踩过,切换瞬间的跳变比持续的低帧率更让人难受。

5.4 调试与可观测性的缺失

方向投影出错时,你至少能画出那条射线,看到它打到了哪里,问题一目了然。ESP 出错时,你面对的是一个可行域计算结果,它可能是一组半空间的交集,你很难直观地看出"为什么这个交集是这个形状""为什么投影到了这个边界"。可观测性的缺失让调试成本大幅上升。

一个务实的缓解手段是给 ESP 加详细的中间结果日志:聚合了哪些约束、求交得到了什么、投影前后的速度分别是多少。这些日志在正常运行时是噪音,但出问题时是唯一的线索。我建议在启用 ESP 的初期就把这套日志埋好,别等出了问题再补。

6. 落地时的取舍:怎么用才不翻车

6.1 分场景启用而非全局替换

基于前面的分析,最稳妥的落地方式是分场景启用。具体做法是:保留方向投影作为默认路径,在检测到多约束并发、高速运动、薄壁接触等特征时,切换到 ESP 路径。切换的判据要简单可靠,比如"当前帧接触约束数量大于 1"或者"速度超过阈值且前方存在薄几何"。

这个方案的好处是,ESP 的风险被限制在它真正能带来收益的场景里,简单场景不受影响。坏处是两套路径的维护成本,以及切换边界的行为一致性需要额外测试。我的经验是,切换判据宁保守勿激进,宁可少用 ESP 也不要让它在不该出现的场景里冒出来。

6.2 给可行域计算加护栏

针对数值脆弱性,护栏是必须的。常见的做法包括:对约束数量设上限,超过就退化;对接近退化的几何配置(平行约束、极小夹角)做特殊处理或者直接退化;对可行域计算结果做合理性校验,比如检查结果是否在物体当前位置的合理邻域内,超出就判定为计算失败并退化。

这些护栏会增加代码复杂度,但它们是把"偶发诡异 bug"变成"可预测的退化行为"的关键。退化行为虽然不完美,但至少是可测试、可解释的。ESP 最怕的不是退化,是那种无法复现的偶发错误。

6.3 仲裁逻辑要显式设计

速度投影和位置修正的矛盾,必须有一套显式的仲裁逻辑,不能靠"谁后执行谁说了算"这种隐式规则。我建议的优先级是:位置合法性优先于速度连续性。也就是说,当两者矛盾时,先保证位置合法,速度的突变可以接受,位置的非法不可接受。

理由很简单:位置非法会引发连锁反应(穿透、卡死、物理爆炸),而速度突变最多是视觉上的顿挫,后者可以靠插值或者平滑处理缓解,前者没法缓解。这个优先级定下来之后,仲裁逻辑就清晰了:位置修正发现非法就强制执行,速度投影的结果在位置修正之后重新计算。

6.4 可观测性先行

前面说过 ESP 调试难,所以可观测性要在启用之前就建好,而不是出了问题再补。具体来说,至少要有:约束聚合的详细记录、可行域计算的中间结果、投影前后的速度对比、退化触发的次数和原因。这些数据在正常运行时可以采样记录,出问题时全量记录。

有了这些数据,ESP 的偶发问题才有可能被定位。没有它们,你面对的就是一个黑盒,只能靠猜。我在项目里见过太多"ESP 偶尔抽风但查不出来"的情况,最后都是靠补日志才找到根因的。

7. 我个人对 ESP 的判断

用了一段时间之后,我对 ESP 的态度是:它是一个方向正确的实验,但现阶段更适合作为特定场景的补充手段,而不是主力方案。"拒绝方向投影"这个主张本身是有洞察力的,它指出了方向投影在多约束和薄几何下的系统性缺陷,这个缺陷是真实存在的,不是吹毛求疵。

但 ESP 用可行域计算来替代方向投影,本质上是把问题从"采样精度"转移到了"聚合精度和数值稳定性"上,而后者目前还不够成熟。它的收益在复杂场景下确实明显,但风险也集中在那些最难调试、最难复现的地方。对于追求稳定的生产环境,我倾向于保守使用;对于场景可控、愿意投入调试成本的项目,它值得一试。

最后一个实操建议:如果你决定上 ESP,先在一个隔离的测试场景里把它跑透,把退化路径、仲裁逻辑、日志体系都验证一遍,再考虑往主流程里接。别一上来就全局替换,那样出了问题你连回退的余地都没有。实验性的东西,就得用实验性的态度对待——给它划好边界,它才能安分地发挥价值。