I would like to create a simple multiplayer game with Node.js and socket.io. My current timing setup is not optimal since the rendering is not smooth. So far, the players are just circles moving around on a canvas.
I try to explain this setup roughly by adding some code snippets, please let me know when more code is necessary.
Each time a client connects to the server, a new player is created and added to a player list. Also, we add listeners for key inputs by the clients (which basically change the direction).
io.on("connection", (socket) => {
const p = playerList.add(socket.id);
socket.on("keydown", (key) => p.addControlKeyDown(key));
socket.on("keyup", (key) => p.addControlKeyUp(key));
}
Also server-side, we have an update loop. Since there is no requestAnimationFrame
in Node.js, I tried to use setInterval
. After each update, we send what is relevant for drawing (this is what p.extract() is for) to all clients.
function updateLoop() {
for (p of playerList.players) {
p.update();
}
const currentPlayers = playerList.players.map((p) => p.extract());
io.emit("playerUpdate", currentPlayers);
}
const interval = setInterval(updateLoop, 30);
I have already played around with the 30 milliseconds here, but it doesn't solve the issue.
On the client, we listen for the updates:
let players = [];
socket.on("playerUpdate", (currentPlayers) => {
players = currentPlayers;
});
On the client we also have the draw loop using requestAnimationFrame
:
function drawLoop() {
clearCanvas();
players.forEach(drawPlayer);
requestAnimationFrame(drawLoop);
}
The communication between the server and the client works, but as you can see, the timing is not really optimal.
How can this be improved?
My first idea was to avoid a client-side loop, since we already have on on the server, and directly send draw request to the clients from there, but this turns out to be even worse.
I am aware that for complex multiplayer games one has to put much more effort into synchronization, and that one also takes snapshots of the game world and actually renders the "past" to the clients. I hope that such complex methods are not necessary for this simple example.