首页 > 解决方案 > 使用 JS、socketio、nodejs 的客户端之间的延迟移动/同步

问题描述

我一直在尝试使用 nodejs、socketio、js 制作一个程序,我可以在 Canvas 中移动一个圆圈,并且所有其他客户端共享该圆圈及其位置。

因此,在任何客户端中移动一个,移动其余的,反之亦然。服务器处理该位置,并将其发送回所有客户端

每个客户端都进行自己的渲染。

问题是,每次我移动时,移动都会变得迟钝。就好像服务器从客户端得到了位置,但有点偏离,然后发出它,循环重复。

同样毫无价值的是,连接的客户端越多,它就越滞后。但即使除了我使用的客户之外没有其他客户,它仍然有点滞后。和服务器实时更新有关。

视频:https ://i.imgur.com/I6pGWo2.gif (总共有 2 个客户端,移动同步但滞后)

视频 2:https ://i.imgur.com/DnfDHme.gif

视频 3:https
://i.imgur.com/Sr7Mbxw.gif服务器代码:

const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
const path = require('path');


app.use('/static', express.static(__dirname + '/static'));

app.get('/', function(request, response) {
    response.sendFile(path.join(__dirname, 'index.html'));
});


let playerLoc = {
    X: 50, 
    Y: 50,
}

function sendNewPos()
{
    io.sockets.emit("newPos", playerLoc); //Send current server location to all clients
}


io.on('connection', socket => {
    console.log("User Connected");

    socket.on("clientPos", newPosData => { //get current Pos from all clients to then sync them together
        playerLoc = newPosData;
    })

});


setInterval(() => {
    console.log("here:" +  playerLoc.Y + " and X: " + playerLoc.X);
}, 22 );

setInterval(() => {
    sendNewPos();
}, 100);



http.listen(3000, () => {
    console.log('listening on *:3000');
});

客户端代码:

//socket.on('num', (counter) => {
//$lst.appendChild(newItem(`hello - ${counter}`));

let player = {
    moveW: false,
    moveD: false,
    moveS: false,
    moveA: false,
}


let playerLoc = {};

var socket = io.connect(); 


socket.on("newPos", newLoc =>{ // get pos from client at the start so playerLoc isn't undefined
    playerLoc = newLoc;
});

function render()
{
    let c = document.getElementById("screen");
    let ctx = c.getContext("2d");
    ctx.clearRect(0, 0, screen.width, screen.height)
    ctx.beginPath();
    ctx.arc(playerLoc.X, playerLoc.Y, 50, 0, 2 * Math.PI); // 100 ix X, 75 is Y (first ix X, second is Y)
    ctx.fillStyle = "black";
    ctx.fill();
    ctx.stroke();
}


document.addEventListener("keydown", event => {
    if (event.keyCode == 87) // w 
    {
        player.moveW = true;
    }

    if (event.keyCode == 68) // d
    {
        player.moveD = true;
    }

    if (event.keyCode == 83) // s
    {
        player.moveS = true;
    }

    if (event.keyCode == 65) // a
    {
        player.moveA = true; 
    }
})


document.addEventListener("keyup", event => {
    if (event.keyCode == 87) // w 
    {
        player.moveW = false;
    }

    if (event.keyCode == 68) // d
    {
        player.moveD = false;
    }

    if (event.keyCode == 83) // s
    {
        player.moveS = false;
    }

    if (event.keyCode == 65) // a
    {
        player.moveA = false; 
    }
})


function move()
{
    if (player.moveW)
    {
        playerLoc.Y -= 5;
       
    }

    if (player.moveD)
    {
        playerLoc.X += 5;
        
    }

    if (player.moveS)
    {
        playerLoc.Y += 5;
        
    }

    if (player.moveA)
    {
        playerLoc.X -= 5;
        
    }
}



setInterval(() => {
    move();
}, 15);

setInterval(() => {
    render();
}, 15);






socket.on("newPos", newLoc =>{ //get newPos from Server
    playerLoc = newLoc;
    console.log("Y: " + playerLoc.Y + "X: " + playerLoc.X);
    console.log("boom");
});

setInterval(() => { //send current Position to server, which the server then uses to send to all clients so positions update correctly
    socket.emit("clientPos", playerLoc)
}, 50);

标签: javascriptnode.jsexpresssocket.io

解决方案


有几个问题导致您看到的内容:

首先,您可能应该使您的服务器具有权威性。这意味着只允许服务器传输玩家的确切 X、Y 坐标。如果 X,Y 的确切位置只有一个事实来源,它将大大减少围绕圆圈所在位置的“战斗”。

客户端不会发送坐标,而是发送诸如“我想向上移动”、“我想停止向上移动”之类的更新——这将允许服务器保持对确切位置的控制,但仍然让客户端对其施加影响.

其次,由于客户端无法完全控制“播放器”对象,当服务器覆盖客户端的本地数据副本时,不可避免地会出现某种程度的抖动。

您可以通过使用插值来平滑过渡到新位置,并从其他数据(例如,服务器发送“位置是 X,Y,此时玩家正在向右移动”)推断它在哪里将成为下一帧。

如果您发送这样的信息(玩家对象移动的方式),您不需要发送太多数据,这将使您的客户端看到更少的打嗝,因为只要从服务器接收到的数据覆盖本地副本,就会发生这些问题具有不同的价值。

例如,服务器可以发送“玩家现在从 X,Y 向左移动”,然后客户端可以自己模拟该移动,这样服务器就不必发送另一个更新,直到其中一个客户端再次按下按钮。

如果您还想变得更复杂,您可以开始测量每个客户端和服务器之间的延迟(发送和接收消息需要多长时间)并使用它来预测数据的偏移量。


推荐阅读