使用物理插值

如何在 Godot 游戏中加入物理插值?会有什么问题吗?

我们尝试将该系统做得尽量易用,很多现有的游戏进行少量修改就能够正常工作。即便如此,在有些情况下还是会需要特殊处理,下文会进行讲解。

开启物理插值设置

第一步是在 ProjectSettings.physics/common/physics_interpolation 中打开物理插值。你现在就可以运行游戏了。

很有可能没什么太大的区别,尤其是你的物理周期是 60 TPS 或者是它的倍数的时候。不过,底层实际发生了很多事情。

小技巧

要将现有的游戏转换为使用插值,强烈建议你暂时将 ProjectSettings.physics/common/physics_fps 调低,比如调到 10,这样就会让插值问题更为显著。

将(几乎)所有游戏逻辑从 _process 移到 _physics_process

物理插值最基本的要求(你可能已经在执行了)就是应当在(物理周期中执行的)_physics_process 而不是(渲染帧中执行的)_process 里进行对象的移动和游戏逻辑。这意味着你的脚本应当在 _physics_process 中执行大段的处理,包括输入和 AI。

只在物理周期中设置对象的变换,就可以让变换在物理周期之间自动进行插值,确保游戏在任何机器上都能达到一致的运行结果。这样做还能带来额外的好处,会减少高 FPS 时的 CPU 占用,因为(例如)AI 逻辑就不必每个渲染帧都运行了。

备注

如果你尝试在物理周期之外设置插值对象的变换,就会插值出错误的位置,导致颤动。这种颤动可能在你的机器上不可见,但是部分玩家是观察到的。因此,应当避免在物理周期外设置插值对象的变换。如果检测到这样的情况,Godot 会尝试在编辑器中进行警告。

小技巧

这只是个软性规定。是存在想要将对象在物理周期之外进行传送的情况的(例如开始关卡或者重生对象时)。但总的来说,你应该在物理周期中应用变换。

确保所有间接移动发生在物理周期中

考虑到在 Godot 中,Node 的移动可能不仅是由你自己的脚本造成的,也有可能是补间、动画、导航自动产生的。如果你想要用这些方法来移动对象(这些方法也能用于控制不进行插值的属性),应当将它们都设置为在物理周期执行,而不是每帧执行(“空闲”)。

备注

请注意节点也可能不是自行移动的,SceneTree 中的父节点也可能将其移动。因此,父节点的运动也应该只在物理周期中进行。

选择物理帧率

使用物理插值时,渲染与物理是脱钩的,你可以选择任何适合你的游戏的值,不再局限于用户显示器刷新率的倍数(达到目标 FPS 时避免游戏中产生卡顿)。

大体的指南:

较低的帧率(10-30)

中等的帧率(30-60)

较高的帧率(60+)

CPU 性能更高

复杂场景下物理行为较好

适合快速物理

输入存在延迟

适合第一人称游戏

适合赛车游戏

物理行为简单

备注

开发时可以随时修改帧率,在项目设置中修改即可。

传送对象时调用 reset_physics_interpolation()

大多数情况下,你都希望在两个物理帧之间进行插值。然而,有一种情况下你可能是不想要的。这就是你最初在放置对象,或者将它们移动到新位置的时候。这里你是不想要在两者之间进行平滑运动的——你想要瞬间移动。

解决方法就是调用 Node.reset_physics_interpolation 函数。你应该在设置位置/变换之后在 Node 上去调用这个函数。其余都是自动为你执行的。

即便你忘记调用,大多数情况下一般也不会有问题(尤其是帧率较高时)。你可以把这些留到打磨游戏的阶段再做。最不济,就是在移动的时候看到几帧连续的移动——看到就知道要做了!

重要

你应当在设置新位置之后调用 reset_physics_interpolation() 而不是之前。否则你还是会看到不想看到的连续运动。

测试与调试技巧

即便你想要以 60 TPS 执行物理,为了彻底测试你的插值,获取最平滑的游戏体验,非常建议临时将物理周期率设为 10 TPS 等较低的值。

游戏体验可能不是非常完美,但是应该能够让你更容易地观察到应当去调用 Node.reset_physics_interpolation 的地方,或者是应当使用自定义插值的地方,例如 Camera。修复了这些情况之后,你就可以将物理周期率设回想要的值了。

使用较低的周期率进行测试的另一大优势在于,你经常可以注意到与物理周期同步的其他游戏系统,造成一些你可能想要避免的跳跃。典型的例子就是设置动画的混合值,你可能会想要在 _process() 里设置并手动进行插值。