简体   繁体   中英

HTML5 Canvas Multiplayer Socket.io Sprite Flickering

I'm using socket.io in combination with Express to render a canvas multiplayer game, I wanted to use sprites and pixel art since the beginning, but when my canvas updates my sprite flickers a bit.

Here's the socket.io server code which first adds the connected player to players dictionary and then checks for movement emit in order to update players' position on the canvas :

    let players = {};

        io.on('connection', function (socket) {

          players[socket.id] = { x: 300, y: 300};

          socket.on('disconnect', function(){
           delete players[socket.id];

          });

         socket.on('movement', function(data) {
          let player = players[socket.id] || {};
          if (data.left) {
            player.x -= 5;
          }
          if (data.up) {
            player.y -= 5;
          }
          if (data.right) {
            player.x += 5;
          }
          if (data.down) {
            player.y += 5;
          }
        });

      });

    // Here the server emits\send the "state" of the canvas 
    // looping passing the dictionary of players

    setInterval(function() {
      io.emit('state', players);
    }, 1000 / 60);

And here's the client, where the problematic code is (imo):

var socket = io();

var width = window.innerWidth;
var height = window.innerHeight;

var movement = {
  up: false,
  down: false,
  left: false,
  right: false
}
document.addEventListener('keydown', function(event) {
  switch (event.keyCode) {
    case 65: // A
      movement.left = true;
      console.log("A");
      break;
    case 87: // W
      movement.up = true;
      console.log("W");
      break;
    case 68: // D
      movement.right = true;
      console.log("D");
      break;
    case 83: // S
      movement.down = true;
      console.log("S");
      break;
  }
});
document.addEventListener('keyup', function(event) {
  switch (event.keyCode) {
    case 65: // A
      movement.left = false;
      break;
    case 87: // W
      movement.up = false;
      break;
    case 68: // D
      movement.right = false;
      break;
    case 83: // S
      movement.down = false;
      break;
  }
});
/* end movements */
let canvas = document.getElementById('canvas');
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext('2d');

socket.on('state', function(players) {

    // Clear the canvas so that the previous positions are erased
    ctx.clearRect(0, 0, width, height);

    // Everytime you update (frame) the state of the canvas draw all players on it
    for (let id in players) {
    let player = players[id];
    // Drawing with fillRect = no flickering
    //ctx.fillRect(player.x, player.y, 100, 100);

    // Drawing my sprite image = flickering!

    let image = new Image();

    image.onload = function() {
     ctx.mozImageSmoothingEnabled = true;
     ctx.webkitImageSmoothingEnabled = true;
     ctx.msImageSmoothingEnabled = true;
     ctx.imageSmoothingEnabled = true;
     ctx.drawImage(image, player.x, player.y);
                            };
    image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAqCAYAAABYzsDTAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAUxJREFUWEftlk0KwjAQhb1CL9EjiD+4cyciint37goiiFAE1y506xkEV56gB/AC4jXcuIq8YmRME5MJqSJUeBT/vnnzOmlSq/3yNes3BDQfNHMthq1cy1H7TV4eK/hbbEmvLiDXWNJxR0BO2ZcKVxzAkU1OptUfvaD340qI07ogfO5Fpo6/Bu92NwJCJ7h6OZcxoAPqPAgcwPN2muceHH47pEIqOPy6T7RwOjXe01LBC6Max7GA5EKS73FF5uQ73phTkA5OC0dRxFtEX4F/ci1juewmPOd02aMA7YRGAjALLsGmmxkETh+3NufO7l0jkfMHcJZluawzyYUDCLA1f07eqktnuEve1ggKu/FzA3bY7e35WqrL54rpRMA2T/9QKhyF1AKmguwu1FOWrhgbSiG6Y5wXUJd3qfBgUZjaDXYTdQX+H15KB7p5t47jAzRfydFOtcn9AAAAAElFTkSuQmCC';
        }

});

setInterval(function() {
  socket.emit('movement', movement);
}, 1000 / 60);

The thing is that whenever I clean the canvas with ctx.clearRect(0, 0, width, height); in my loop, I get some flickering, but only when using an image, if I use canvas shapes I get none.

Why is that so?

How do I avoid that flickering? I really need to use sprites and simple shapes are a no-no.

Is there a better way to "loop" in multiplayer using socket.io that I'm missing? (I read about animationframe but I didn't really understand how to implement it with socket.io).

Any little help is appreciated I've been struggling a lot with it.

You actually have a larger problem in your code than this small flickering.
You are sending image data over the network every 60th of a second.
You won't be able to reach 60FPS animation on every end with such logic.

The first thing to do, is to create an assets manager, where you will load the required spritesheets for the game.

In this case, it seems to be some skins. So load once an HTMLImage with a sprite-sheet that will contain all your skins sprites before the game even starts and make this HTMLImage available for your rendering loop, eg by setting it as a global of your game's function.

const assets = {
  skin: new Image();
};
assets.skin.onload = e => tellTheGameWeAreReady();
assets.skin.src = 'path/to/skin_spritesheet.png';

Then what you will send over the socket is only the coordinates inside this sprite-sheet that the other players will have to draw.

Since your HTMLImage containing this sprite-sheet will already have loaded, all you have to do in the socket.on('state' handler is to draw the corresponding part of the sprite sheet at given coordinates.

socket.on('state', function(players) {
  ctx.clearRect(0, 0, width, height);
  for( let id in players ) {
    let player = players[id];
    /*//socket should emit each 'player' as 
    {
      x: x_position of player in world,
      y: y_position of player in world,
      skin: {
        x: x_position of to be displayed sprite in the sprite-sheet
        y: y_position of to be displayed sprite in the sprite-sheet
        w: width of to be displayed sprite in the sprite-sheet
        h: height of to be displayed sprite in the sprite-sheet
      }
    }
    */
    ctx.drawImage( assets.skins, 
      player.skin.x,
      player.skin.y,
      player.skin.w,
      player.skin.h,
      player.x,
      player.y,
      player.skin.w,
      player.skin.h
    );
  }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM