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