多人游戏中的移动同步

8

我正在开发一个多人游戏,但在同步玩家方面遇到了问题。

当玩家按下移动键(W、A、S、D)时,客户端会发送有关按下按钮的数据包,服务器根据按下的键设置速度,并将新速度发送回所有附近的玩家。

当玩家释放键时,客户端会发送一个数据包,服务器将玩家速度设置为0,0,并将位置和速度发送给所有附近的玩家。

所以问题是当我释放键时,大部分时间玩家会跳回去。

我该如何解决这个问题?

我正在使用socket.io。

客户端:

   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()});
        }
    });

服务器端:

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());
});

1
“我该如何修复这个问题?”... 没有任何代码,我们怎么帮助你呢? - woofmeow
抱歉,没错,我会进行编辑。 - Dávid Szabó
作为建议,在jQuery事件处理程序中使用e.which - jQuery将浏览器不一致的检索键代码规范化为e.which - Ian
不,玩家不会因为延迟而跳回。客户端移动并发送数据包到服务器,100毫秒后,服务器接收此数据包,然后移动玩家(在服务器端)。因此,客户端上的玩家比服务器提前100毫秒。当用户停止时,只需停在那里。100毫秒后,服务器开始停止。您可以使用两台电脑运行MMORPG游戏,观察玩家移动。 - jean
1个回答

16

你正在从事一项非常复杂的任务,这是我在一个宠物项目中完成的一部分;)

你正在开发一个客户端-服务器体系结构的游戏,因此服务器是游戏逻辑和决策的最终权威。你目前处理渲染的方式会因为延迟而导致速度和方向的突然变化(正如你所注意到的那样!)

诀窍是缓存远程玩家的移动信息,所以始终以略微延迟的方式渲染玩家。在我的项目中,我保持了原始的简单性,只使用位置数据,而不是加速度或速度。例如,当玩家A在他的机器上移动时,不会立即发送命令以接收确认,他会移动,并在我的网络发送循环的下一个刻度(每秒10个刻度)时将其位置发送给服务器,服务器使用这个新位置更新所有附近的客户端。这些客户端为每个“远程”玩家存储一个缓冲区,该缓冲区在呈现更新之前存储每个位置100毫秒的时间。通过这种方式,客户端会以略微延迟的方式呈现,但我可以在每个位置坐标之间插值(平滑精灵/模型的过渡),以实现平稳的运动和速度加速的错觉。

这是客户端代码的基本插值函数。该系统最多只为每个远程玩家排队两个更新,其中更新数组中的索引0是两个更新中较旧的一个。因此,索引0可能是远程玩家位置0毫秒,而索引1则是远程玩家位置100毫秒。

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);
  }
},
在那段代码中,我接收到了大约间隔100毫秒的更新,所以在时间0时刻位置是s1,在100毫秒时刻位置是s2。因此,如果50毫秒已经过去,自我们收到s2以来,实体就在两个位置之间的50%。这对我的需求来说很好,但可能在其他类型的游戏中不起作用或需要调整。
这些资源是解释网络游戏和处理延迟的绝佳起点,实现插值和外推将对远程客户端平滑度产生惊人的差异。 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 <- 真的很好! https://developer.valvesoftware.com/wiki/Lag_compensation 祝你好运 :)

我需要为我正在编写的小游戏制作类似的东西 :) 谢谢!我会尝试并给你们反馈(如果它运行良好,我可能会写一篇关于此的博客文章) - Loic Coenen
@LoicCoenen 你的实验进展如何?我希望你能写一篇关于你的项目以及如何在客户端-服务器架构中使其工作的博客。让玩家流畅地移动和互动确实是一个挑战。 - newguy
不错的想法。也许我应该做类似的事情。这是一个有趣的话题。 - Evan Shortiss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接