2

我正在开发一个浏览器多人游戏,其中每个客户端都插入(线性)由服务器发送的实体帧。它在高帧速率(> 30fps)时看起来不错,但以较低的帧速率抖动(< 30fps)并且冻结并跳跃并且帧速率非常低(< 10fps)。我想降低帧率,并且我知道这是可能的(请参阅以10fps发送更新的Brutal.io)。多人游戏浏览器:线性插值造成抖动和跳跃

这是基本的算法,我使用的:

  • 服务器在帧率发送更新(比如,10fps的)
  • 客户端渲染游戏在帧率(比如,60fps的)
  • 客户端在屏幕上不更新实体直匹配服务器的数据了:这会显得风声鹤唳
  • 相反,它使用线性插值函数来消除服务器更新之间的帧
  • 以10:60fps为例,客户端将渲染6帧以创建平滑动画
  • 它通过测量服务器更新与客户端渲染帧之间的差异(差异)来实现此目的
  • 然后得到一个乘法器由潜水客户增量由服务器增量
  • 然后,它调用线性插值函数,使用屏幕的位置,服务器的位置,和乘法器,以生成一个新的屏幕位置

该片段不包含特定的代码,但应该足以证明基本概述(请参阅代码中的注释以获取信息):

var serverDelta = 1; // Setting up a variable to store the time between server updates 

// Called when the server sends an update (aiming for 10fps) 
function onServerUpdate(message) { 
    serverDelta = Date.now() - lastServerFrame; 
} 

// Called when the client renders (could be as high as 60fps) 
var onClientRender() { 
    var clientDelta = Date.now() - lastUpdateFrame; 

    // Describes the multiplier used for the linear interpolation function 
    var lerpMult = clientDelta/serverDelta; 
    if (lerpMult > 1) { // Making sure that the screen position doesn't go beyond the server position 
     lerpMult = 1; 
    } 
    lastUpdateFrame = Date.now(); 

    ... 

    // For each entity 
    // ($x,$y) is position sent by server, (x,y) is current position on screen 
    entity.x = linearInterpolate(entity.x, entity.$x, lerpMult/2); 
    entity.y = linearInterpolate(entity.y, entity.$y, lerpMult/2); 
} 

function linearInterpolate(a, b, f) { 
    return (a * (1 - f)) + (b * f); 
}; 

如上所述,这会在运动中产生抖动和跳跃。有什么我做错了吗?我将如何使这个动作顺利?

+0

我想明白 - 你怎么知道是什么最后的服务器框架(在插值中)在它发生之前?换句话说,如何在一个值和另一个不知道的值之间进行插值,而不会造成滞后? – clabe45

+0

嗯,好吧,你是说我做这件事的方式是使用前一帧中的增量与当前值进行插值?解决方案是存储“最后的服务器增量”并使用它吗? –

+0

@ clabe45对不起,忘了提及你。见上面^^^。 –

回答

2

插值必须在两个服务器状态之间。您可以保留客户端上收到的最后一个X服务器状态的历史记录。每个服务器状态代表一个特定的框架

例如,假设你的客户保持以下服务器状态及其框架:

state[0] = {frame: 0, ... }; 
state[1] = {frame: 10, ... }; 
state[2] = {frame: 20, ... }; 

如果客户现在渲染帧15,那么就必须插值的位置state[1]state[2]之间的中途。其计算公式为

// prev=1, next=2 
let interpolatePercent = (clientFrame - serverState[prev].frame)/serverUpdateRate; 
entity.x = interpolatePercent * (serverState[next].entity.x - serverState[prev].entity.x) + serverState[prev].entity.x; 

在你的代码,lerpMult很可能大于1,在这种情况下,你现在所做的推断,而不是插这是更难。

而且你可能也想看看这实现了浏览器的多人游戏插补(和外推)的开放源码库:https://github.com/lance-gg/lance