Easel全新定制物理引擎:增量回滚功能让大型多人游戏开发成为可能!
Easel全新定制物理引擎:增量回滚功能让开发大型多人游戏成为可能!
2026年5月1日,阅读时长9分钟。raysplaceinspace,Easel创作者。
我们期望Easel强大到足以制作出让玩家沉浸数小时的游戏。像《Among Us》这类热门多人游戏,玩家能在整个宇宙飞船中四处走动、完成任务并躲避冒名顶替者。可惜的是,到目前为止,这种规模的游戏对于Easel而言还难以实现。因为现有的物理引擎若要支持Easel的预测性多人游戏架构,就必须对整个世界进行快照和回滚,而每帧都这样做的工作量太大。
在此之前,你只能把游戏世界设计得“小一些”。但“现在不同了”!
Easel全新的“定制物理引擎”仅对世界中“发生变化的部分”进行快照和回滚。那艘巨大的宇宙飞船或许由数千个物体组成,像墙壁、控制面板、通风口等。然而,每帧中实际发生变化的物体数量少得惊人,玩家在世界中走动和互动时,每帧可能只有不到30个物体发生变化。智能的实现方式能让屏幕外的物体处于休眠状态。
每帧只需对数千个物体中的30个进行快照,比之前减少了30 - 50倍,这使使用Easel开发大型世界的多人游戏突然变得可行了,放手去做吧!
幕后原理
Easel的新物理引擎是为Easel量身定制的,所以它支持Easel的所有功能,且表现更出色!下面介绍一下它的一些独特特性。
休眠机制
做一件事最快的方法就是根本不做。当一个物体处于休眠状态时,在它再次唤醒之前,无需进行任何快照、回滚或物理计算。和其他引擎等待物体静止几秒后才进入休眠不同,Easel会在物体速度降为零(当然,是在一个小的误差范围内)时立即让其进入休眠状态。
这里有个棘手的情况是“重力”,它持续的作用力可能会让整个世界都保持活跃。Easel会跟踪每个物体上的力和反作用力,判断它们是否平衡。无论物体速度是否为零,只要堆叠中的一个物体受力不平衡,整个堆叠就未达到平衡状态,因此整个堆叠都会保持活跃。
空间索引
和许多其他物理引擎一样,Easel的“宽相位检测”使用了边界体积层次结构(Bounding Volume Hierarchy,BVH)来快速查找潜在的碰撞。Easel的BVH算法经过优化,可最大程度减少不必要的快照和回滚,仅在树结构发生变化时进行增量重新平衡。这就如同你在朋友来访前才打扫房间一样,Easel的BVH同样“高效”。
另一个技巧是,Easel的BVH还会跟踪每个碰撞体的 [类别](/docs/learn/physics/categories),这能显著加快常见的游戏查询速度。例如,机器人经常需要定位最近的玩家,如果玩家在地图的另一端,它就不得不遍历世界中的每个碰撞体来找到最近的玩家。有了类似金属探测器的功能,在众多 `Category:Haystack` 碰撞体中找到最近的 `Category:Needle` 碰撞体就快多了。
移动机制
在物理引擎中,让角色移动看似简单,实则相当复杂。常见的方法是在移动时增加速度,物理模拟完成后再减去这个速度,但当角色在移动过程中撞到障碍物时,问题就出现了。
即使物理引擎正确地将速度归零,但之后减去之前增加的移动速度时,又会重新引入物理引擎刚刚消除的反弹,让角色感觉非常容易反弹。
- 有些游戏通过“阻尼”消除所有速度来解决这个问题,这样虽然消除了反弹,但也消除了击退效果,减少了游戏的趣味性。撞到墙时没有反弹,这没问题;但被火球击中时没有击退效果,就感觉不太对了。
- 其他游戏则通过基于射线检测创建“运动学角色控制器”来解决。在物理引擎术语中,“运动学”(与静态或动态相对)意味着角色不受力和碰撞的影响。换句话说,物理引擎未能产生预期的结果,因此完全被绕过了。“物理引擎,你就干好你的活啊。物理计算,好好做。别这样,算了,我自己来吧。”
Easel物理引擎的求解器直接内置了无反弹移动功能。只需使用 [ForcefulStep](/docs/reference/physics#forcefulstep) 并设置新的 `restitution = 0` 参数,Easel的新物理引擎就能确保移动不会产生反弹。
它是如何工作的呢?
诀窍在于,Easel处理移动的方式与处理位置重叠的方式类似。
你是否玩过这样的游戏:角色卡在墙里,物理引擎试图将你“弹出”,结果把你送到地图的另一端?这是常见的物理引擎故障。《超级马里奥64》的速通玩家就利用这样的故障让马里奥背朝下滑上楼梯。哎哟!
现代物理引擎通过单独解决“位置校正”问题来避免这个问题。将角色从墙中弹出的力会立即应用到位置上,而不改变速度,这意味着“弹出速度不会持续到下一帧”。
Easel中的 `ForcefulStep` 作为位置校正的一部分实现,这就是它不会导致反弹的原因,除非你希望它反弹。我们一举两得!
实际上,由于多种原因,情况要复杂一些。Easel首先同时求解弹出 + 移动和速度,然后存储弹出速度,接着移除弹出 + 移动约束并再次求解,存储稳定后的速度。此时,物体还没有移动,所以我们现在可以使用正确的速度来扫描下一次碰撞的时间。到移动的时候,我们使用弹出速度,但在帧结束时只应用稳定后的速度,这样反弹就消失了。
换句话说,Easel提前收集所需的所有数据,而不进行更改,这样在真正移动时,就能做出正确的操作。
连续碰撞检测
我们喜欢看到两个火球在半空中相撞的场景。
要检测两个快速移动的物体之间的碰撞,需要进行连续碰撞检测,因为如果我们每帧只检查一次碰撞,可能会错过两个火球重叠的精确时刻。
和其他物理引擎一样,Easel通过扫描和形状投射来进行连续碰撞检测,但在这个过程中,我们发现Easel与其他物理引擎有一些不同之处,这可能会让一些“游戏开发者”感兴趣:
- **Rapier** 物理引擎在火球在帧开始时接触镜子的情况下可能会产生错误的结果。在Easel中,碰撞会先得到解决,使火球反弹并改变方向,“然后” 再查找两个火球的碰撞时间。而在Rapier中,它会先使用原始速度查找碰撞时间,这意味着在这种情况下,它会以错误的方向扫描火球。这种差异是因为Rapier过早进行位置积分,在知道碰撞时间之前就确定了子步长。Easel提前存储了足够的信息,能够在知道碰撞时间后再进行位置积分,从而避免了这个问题。
- **Box2D 2.4** 使用正确的速度进行扫描,但采用了先进行完整的常规物理模拟,然后回溯的方法,这就是为什么在连续碰撞检测之前所有碰撞都已经解决。有趣的是,新的 **Box2D 3.0** 根本不支持动态对动态的连续碰撞检测,这意味着那两个火球就像夜空中的两艘船一样会错过彼此。也许这是Box2D未来的发展方向,除此之外,他们似乎通过使用推测性接触实现了避免子步长的理想状态。
- **Photon Quantum** 是一款专业的多人回滚网络代码引擎,它根本不支持连续碰撞检测,理由是其物理引擎是无状态的,实现起来成本太高。看来我们物理状态的增量快照和回滚功能让Easel能够高效地支持这一特性。
物体可自行移动
之前的物理引擎有一个不便的边缘情况,即带有 `velocity` 或 `turnRate` 但没有碰撞体的物体根本不会移动。
可以说这是合理的。如果没有质量来保持动量,就不会有运动,但我们不仅仅是在制作一个“物理” 引擎,而是在制作一个“游戏” 引擎。有时候,物体只是将精灵组合在一起的一种方式,物理特性并不重要。
现在,如果你给一个物体设置 `velocity` 或 `turnRate`,即使它没有碰撞体,也会自行移动。附上一个 `TextSprite`,你就可以让一个简单的广告牌在屏幕上向上漂浮,滚动到遥远的星系。
旁注:光子没有质量,但它们仍然有速度,事实上,现在每秒有 `10^17` 个光子正撞击你的眼睛,所以也许有些物理引擎需要回归现实。
致谢
Easel的物理引擎基于 [Parry](https://parry.rs) 的碰撞检测算法构建,Parry是Dimforge开发的优秀开源库,为 [Rapier](https://rapier.rs) 物理引擎提供支持。
开发一个物理引擎是一项艰巨的任务。为了让它简洁、高效,并与Easel的多人游戏架构良好配合,我们做了许多细微的决策。现在,不仅物理引擎,Easel的所有部分都只对发生变化的部分进行快照和回滚,这意味着你可以创建更大的游戏世界。
是时候大胆设想了!
标签:
- [更新](/blog/tags/updates)
[上一篇文章 子相机与火花效果(2026年4月更新)](/blog/2026-apr-update)
- 幕后原理
- 休眠机制
- 空间索引
- 移动机制
- 连续碰撞检测
- 物体可自行移动
- 致谢
政策
- [服务条款](/terms)
- [隐私政策](/privacy)
- [行为准则](/conduct)
Easel
- [博客](/blog)
- [媒体资料包](/press)
- [联系我们](/contact)
[](https://discord.gg/EJfzutBSz8 "加入Easel的Discord社区")[](https://www.reddit.com/r/MadeWithEasel "在Reddit上关注Easel")[](https://x.com/madewitheasel "在Twitter上关注Easel")[](https://www.facebook.com/MadeWithEasel "在Facebook上关注Easel")[](https://www.tiktok.com/@madewitheasel "在TikTok上关注Easel")[](https://youtube.com/@MadeWithEasel "在YouTube上关注Easel")
