最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Multiplayer game movement synchronization - Stack Overflow

programmeradmin5浏览0评论

I'm working on a multiplayer game and i'm having problem with synchronizing players.

When a player presses one of the move keys (W,A,S,D) then the client sends a packet about the pressed button, the server sets the velocity according to the pressed key and sends back to all nearby players the new velocity.

When the player release the key then the clients sends a packet, server sets the player velocity to 0,0 and sends the position and velocity to all nearby players.

So the problem is when i release the key, then most of the time the player jumps back.

How could i fix this?

i'm using socket.io.

Client side:

   socket.on('positionEntity', function (data) {
        console.log((data.x - entities[data.id].x)+" "+(data.y - entities[data.id].y));
        entities[data.id].setPosition(data);
    });

    $(document).keyup(function(e) {
        if (e.keyCode == 87) {
            keys.W = false;
            socket.emit("stopMove", {dir: 0, time: Date.now()});
        }

        if (e.keyCode == 65) {
            keys.A = false;
            socket.emit("stopMove", {dir: 1, time: Date.now()});
        }

        if (e.keyCode == 83) {
            keys.S = false;
            socket.emit("stopMove", {dir: 2, time: Date.now()});
        }

        if (e.keyCode == 68) {
            keys.D = false;
            socket.emit("stopMove", {dir: 3, time: Date.now()});
        }
    });

    $(document).keydown(function(e) {
        if (e.keyCode == 87 && !keys.W) {
            keys.W = true;
            socket.emit("startMove", {dir: 0, time: Date.now()});
        }

        if (e.keyCode == 65 && !keys.A) {
            keys.A = true;
            socket.emit("startMove", {dir: 1, time: Date.now()});
        }

        if (e.keyCode == 83 && !keys.S) {
            keys.S = true;
            socket.emit("startMove", {dir: 2, time: Date.now()});
        }

        if (e.keyCode == 68 && !keys.D) {
            keys.D = true;
            socket.emit("startMove", {dir: 3, time: Date.now()});
        }
    });

Server side:

socket.on('startMove', function(data) {
    if (data.dir == 0) socket.player.setMotionY(-5);
    if (data.dir == 1) socket.player.setMotionX(-5);    
    if (data.dir == 2) socket.player.setMotionY(5);
    if (data.dir == 3) socket.player.setMotionX(5);

    io.sockets.emit("positionEntity", socket.player.serializePosition());
});

socket.on('stopMove', function(dir) {
    socket.player.setMotionX(0);
    socket.player.setMotionY(0);

    io.sockets.emit("positionEntity", socket.player.serializePosition());
});

I'm working on a multiplayer game and i'm having problem with synchronizing players.

When a player presses one of the move keys (W,A,S,D) then the client sends a packet about the pressed button, the server sets the velocity according to the pressed key and sends back to all nearby players the new velocity.

When the player release the key then the clients sends a packet, server sets the player velocity to 0,0 and sends the position and velocity to all nearby players.

So the problem is when i release the key, then most of the time the player jumps back.

How could i fix this?

i'm using socket.io.

Client side:

   socket.on('positionEntity', function (data) {
        console.log((data.x - entities[data.id].x)+" "+(data.y - entities[data.id].y));
        entities[data.id].setPosition(data);
    });

    $(document).keyup(function(e) {
        if (e.keyCode == 87) {
            keys.W = false;
            socket.emit("stopMove", {dir: 0, time: Date.now()});
        }

        if (e.keyCode == 65) {
            keys.A = false;
            socket.emit("stopMove", {dir: 1, time: Date.now()});
        }

        if (e.keyCode == 83) {
            keys.S = false;
            socket.emit("stopMove", {dir: 2, time: Date.now()});
        }

        if (e.keyCode == 68) {
            keys.D = false;
            socket.emit("stopMove", {dir: 3, time: Date.now()});
        }
    });

    $(document).keydown(function(e) {
        if (e.keyCode == 87 && !keys.W) {
            keys.W = true;
            socket.emit("startMove", {dir: 0, time: Date.now()});
        }

        if (e.keyCode == 65 && !keys.A) {
            keys.A = true;
            socket.emit("startMove", {dir: 1, time: Date.now()});
        }

        if (e.keyCode == 83 && !keys.S) {
            keys.S = true;
            socket.emit("startMove", {dir: 2, time: Date.now()});
        }

        if (e.keyCode == 68 && !keys.D) {
            keys.D = true;
            socket.emit("startMove", {dir: 3, time: Date.now()});
        }
    });

Server side:

socket.on('startMove', function(data) {
    if (data.dir == 0) socket.player.setMotionY(-5);
    if (data.dir == 1) socket.player.setMotionX(-5);    
    if (data.dir == 2) socket.player.setMotionY(5);
    if (data.dir == 3) socket.player.setMotionX(5);

    io.sockets.emit("positionEntity", socket.player.serializePosition());
});

socket.on('stopMove', function(dir) {
    socket.player.setMotionX(0);
    socket.player.setMotionY(0);

    io.sockets.emit("positionEntity", socket.player.serializePosition());
});
Share Improve this question edited Aug 7, 2013 at 22:44 Brigand 86.3k20 gold badges167 silver badges173 bronze badges asked Aug 7, 2013 at 22:29 Dávid SzabóDávid Szabó 2,2472 gold badges17 silver badges26 bronze badges 4
  • 1 "How could i fix this?" ... well how do we help you without any code ? – woofmeow Commented Aug 7, 2013 at 22:31
  • Aww sorry true, gonna edit it. – Dávid Szabó Commented Aug 7, 2013 at 22:32
  • As a suggestion, use e.which inside jQuery event handlers - jQuery normalizes browser inconsistencies in retrieving the key code into e.which – Ian Commented Aug 7, 2013 at 22:48
  • No, players will not jump back because latency. client move and send package to server, after 100ms, server recv this package then move the player(on server side). So the player on client ahead 100ms than server. when user stop, just stop there. after 100ms server begin stop. You can use two pc run mmorpg game, watch players move – jean Commented Aug 13, 2015 at 1:16
Add a comment  | 

1 Answer 1

Reset to default 16

This is a very complex task you are working on and something I've done as part of a pet project ;)

You're working on a client-server architecture game so the server is final authority on game logic and decisions. They way you are currently handling rendering will make sudden changes in velocity and direction apparent due to latency (as you have noticed!)

The trick is to buffer movement information of remote players so you always render player with a slight delay. I kept things primitive in my project and used only positional data, not acceleration or velocity. Example, when player A moves on his machine a command is not instantly sent to receive acknowledgement, he moves and on the next tick of my networking send loop (10 ticks per second) his position is fired to the server who updates all clients in the vicinity with this new position. These clients have a buffer for each "remote" player which stores each position for a time (100 milliseconds) before rendering the update. In this way the client is rendered with a slight delay but I can interpolate (smooth the transition of the sprite/model) between each positional co-ordinate to achieve smooth motion with the illusion of velocity and acceleration.

A BASIC interpolation function for client code. This system only queued two updates for each remote player at most, where index 0 in the update array was the older of two. So index 0 might be the remote player position 0ms and index 1 is the remote player position at 100ms.

interpolate: function() {
  var timeDifference = new Date().getTime() - this.serverUpdates[1].time;
  // Percentage of time passed since update was received (I use 100ms gaps)
  var interPercent = (timeDifference) / 100;

  // Latest updates (index 1 is the newest state)
  var s1 = this.serverUpdates[0],
    s2 = this.serverUpdates[1];

  // Need to lerp between values provided in latest update and older one
  var p = (new Vector3(s2.position)).subtract(new Vector3(s1.position));
  p = p.timesScalar(interPercent);

  // New position is the older lerped toward newer position where lerp 
  //percentage is the time passed 
  this.position = new Vector3(s1.position).add(p);

  // Now update rotation in a smooth manner
  var rotationDifference = (s2.rotation - s1.rotation);
  if (rotationDifference && rotationDifference != 0) {
    this.rotation = s1.rotation + (rotationDifference * interPercent);
  }
},

In that code I was receiving updates that were approx 100ms apart, so at time 0 position is s1 and time 100ms s2 is the position. So if 50ms have passed since we received s2 then the entity is 50% between the two positions. This was fine for my need but may not work out in other types of games or might need tweaking.

These resources are an excellent start to explain networked games and dealing with latency, you'll be amazed at the difference implementing interpolation and extrapolation will have on your game smoothness in remote clients.

http://gafferongames.com/networking-for-game-programmers/what-every-programmer-needs-to-know-about-game-networking/

https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization <- REALLY GOOD!

https://developer.valvesoftware.com/wiki/Lag_compensation

Good luck :)

发布评论

评论列表(0)

  1. 暂无评论