javascript - 使用 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);
解决方案
有几个问题导致您看到的内容:
首先,您可能应该使您的服务器具有权威性。这意味着只允许服务器传输玩家的确切 X、Y 坐标。如果 X,Y 的确切位置只有一个事实来源,它将大大减少围绕圆圈所在位置的“战斗”。
客户端不会发送坐标,而是发送诸如“我想向上移动”、“我想停止向上移动”之类的更新——这将允许服务器保持对确切位置的控制,但仍然让客户端对其施加影响.
其次,由于客户端无法完全控制“播放器”对象,当服务器覆盖客户端的本地数据副本时,不可避免地会出现某种程度的抖动。
您可以通过使用插值来平滑过渡到新位置,并从其他数据(例如,服务器发送“位置是 X,Y,此时玩家正在向右移动”)推断它在哪里将成为下一帧。
如果您发送这样的信息(玩家对象移动的方式),您不需要发送太多数据,这将使您的客户端看到更少的打嗝,因为只要从服务器接收到的数据覆盖本地副本,就会发生这些问题具有不同的价值。
例如,服务器可以发送“玩家现在从 X,Y 向左移动”,然后客户端可以自己模拟该移动,这样服务器就不必发送另一个更新,直到其中一个客户端再次按下按钮。
如果您还想变得更复杂,您可以开始测量每个客户端和服务器之间的延迟(发送和接收消息需要多长时间)并使用它来预测数据的偏移量。
推荐阅读
- django-rest-framework - DRF 错误消息结合模型和自定义字段错误消息作为响应
- angular - 可拖动表单并将数据保存到 json 文件
- struts2 - struts2的json插件返回的json包含被转义的特殊字符
- javascript - Firebase Firestore 数据库使用 JS 错误:未捕获(承诺)ReferenceError:文档未定义
- postgresql - Postgres pgAdmin:无法连接到服务器:超时已过期
- python - 烧瓶导入本地文件
- sql - Oracle multple rows to single row
- reactjs - 焦点选择元素
- javascript - HTML5 拼图功能变更
- c - 函数工作 ramdonly、覆盖内存、双重释放、损坏 (!prev)