告别延迟拖尾:用线性预测实现 Display 实体的低延迟跟随
该方法存在缺陷
该方法并不对任意模型生效。详情见方法局限性和编者注。
搞过原版枪械或者自定义 HUD 的人都知道,想让一个 item_display 死死跟在玩家视角前面有多难。
最粗暴的做法是每 tick 执行 tp @e ^ ^ ^。但这有个两难的问题:如果不给 teleport_duration,实体移动没有插值,只有 20fps 的刷新率,看着非常卡顿;如果给了 teleport_duration: 1 强行做平滑,因为客户端播动画需要耗时(50ms),只要玩家一跑动或者转头,模型就会慢半拍,产生极其难受的拖尾。
这篇文章分享一个思路,通过利用客户端渲染特性和简单的线性运动预测,把这个延迟基本“吃掉”。
1. 视角旋转:别用服务端算
很多人试图用高频 tp 来强行同步玩家的视线角度,但这部分其实完全可以白嫖客户端本身的渲染机制。
给 Display 实体加上 billboard: "center",它就会永远在客户端层面自动面朝玩家屏幕。如果想让模型偏离屏幕中心(比如把枪放在右下角),去调 transformation.translation 矩阵,绝对不要用 ^ ^ ^ 算出来的绝对坐标去控制偏移。
把旋转交给客户端后,无论玩家鼠标甩得多快,模型在屏幕上的相对位置都是绝对静止的,零延迟。
2. 移动跟随:预测下一帧
视角的问题解决了,剩下就是坐标的移动。为什么加了插值就会拖尾?因为当服务端命令实体往当前坐标走的时候,客户端需要花时间播动画。等它走到了,玩家早就走到下一个位置了。
所以核心思路是:预测。假设玩家在一瞬间是在做匀速直线运动,我们不让实体飞向玩家当前的位置,而是直接把它 tp 到玩家下一 tick 要去的位置。这样客户端慢吞吞播插值动画的时候,刚好能跟玩家实际的移动轨迹重合。
公式很简单:设玩家上一 tick 的位置是
运行流程也就变成了:每 tick 算出
3. 宏指令里的坐标减法(空间翻折法)
我们可以利用宏来实现。在原版命令里,坐标加法用宏很好写,positioned ~$(B_x) ~$(B_y) ~$(B_z) 就行。但减法没法直接写,因为如果 ~-$(A_x) 就会变成非法的 ~--50,直接报错。
这里分享一个利用局部坐标配合视角旋转来做纯几何减法的技巧:不用管正负号,直接把局部坐标轴“翻过去”对准全局坐标的负方向。
# 假设 target_xyz 代表当前位置 B
# prev_xyz 代表上一帧位置 A
positioned 0. 0. 0. rotated 180 0 run function latch:follower/__tick__/run_at_owner_eyes_tp with storage latch:io temp.vars:
$execute \
positioned ~$(target_x) ~$(target_y) ~$(target_z) \
positioned ~$(target_x) ~$(target_y) ~$(target_z) \
positioned ^$(prev_x) ^ ^$(prev_z) \
rotated 0 90 positioned ^ ^ ^$(prev_y) \
run function latch:follower/tp_here原理解释:
- 前两次
positioned ~$(target_x)...是普通的坐标叠加,算出了。 - 此时执行环境由于开头的设定,是
rotated 180 0(水平向后转)的视角。在这个视角下,局部坐标的左方(^X)和前方(^Z)刚好对应全局坐标的 -X 和 -Z。所以执行positioned ^$(prev_x) ^ ^$(prev_z)时,不管传进来的参数是正是负,都等价于在全局坐标里减去了和 。 - 同理,接着用
rotated 0 90垂直低头看地,局部坐标的前方(^Z)就对应了全局的 -Y。再执行positioned ^ ^ ^$(prev_y)就相当于减去了。
利用这种空间翻折,短短几行代码就搞定了带负数的精确的向量减法。
4. 方案的局限
当然,预测算法不是万能的,这套方案在实际游玩中有两个客观存在的小毛病:
- 急停过冲:因为是根据历史速度盲猜的,当玩家疾跑中突然停下时,实体会因为“惯性”往前稍微冲一下,下一 tick 才会弹回正确位置。
- 吃 TPS / 帧数:这种做法高度依赖稳定的 tick 轴。如果服务器卡顿或者客户端严重掉帧,预测轨迹就会出现视觉偏差(不过说实话,服务器卡的时候,传统的直接 tp 方案一样没法看)。
抛开这两点,这套方法目前在单人地图里效果是还不错的,推荐在开发第一人称手臂模型之类的东西的时候尝试一下。
编者注
该方法仅对原版默认物品模型有最佳效果,使用自定义物品模型需要保证billboard旋转枢轴点在玩家眼部位置。
至于如何保证这一点,此原理暂未探明,留给读者探索;