Socket.io 多人游戏-同步球

Socket.io 多人游戏-同步球,socket.io,webrtc,game-physics,phaser-framework,Socket.io,Webrtc,Game Physics,Phaser Framework,我正在用HTML5开发一个非常简单的游戏,带有惊人的phaser.io。游戏非常简单:一个1vs1截击游戏在2D。。。实际上,它是伟大的“皮卡丘凌空抽射”(pikachu凌空抽射)的“复制品” 正如我所说,游戏非常简单,我有: 玩家1 玩家2 静态元素 球 我必须控制: -球员动作 -球的运动(实际上由街机物理控制) -球员球碰撞 我这样做只是为了好玩,把我朋友的脸加入到玩家中。 这个游戏玩得很好,我和朋友玩得很开心。所以我想,为什么不让它成为在线多人游戏,这样我们就可以从不同的地点远程玩

我正在用HTML5开发一个非常简单的游戏,带有惊人的phaser.io。游戏非常简单:一个1vs1截击游戏在2D。。。实际上,它是伟大的“皮卡丘凌空抽射”(pikachu凌空抽射)的“复制品”

正如我所说,游戏非常简单,我有:

  • 玩家1
  • 玩家2
  • 静态元素
我必须控制: -球员动作 -球的运动(实际上由街机物理控制) -球员球碰撞

我这样做只是为了好玩,把我朋友的脸加入到玩家中。 这个游戏玩得很好,我和朋友玩得很开心。所以我想,为什么不让它成为在线多人游戏,这样我们就可以从不同的地点远程玩

在阅读了有关多人HTML5游戏的内容后,我开始使用带有nodeJS服务器的websocket(socket.io)开发它。io的实现非常简单,通信工作正常

关键是要让游戏真正可玩

以下是我的步骤:

在客户端连接上,将创建它:

  • 玩家1(我自己)
  • 静态元素
客户端等待创建新的客户端连接:

  • 遥控器
因此,连接两个客户端后,在每个客户端上我都有:

  • 自有玩家
  • 遥控播放器
  • 静态元素
然后游戏开始了。。。在这个阶段,这是不公平的,因为它根本不动。因此,为了让远程播放器移动(经过一些尝试),我决定实现一种权威服务器,这样工作

  • 本地玩家移动(输入注册)->客户端将输入发送到服务器->服务器将输入发送到两个客户端->每个客户端应用移动(通过更改速度)
这种机制使球员的动作在过去的基础上进行,但“同步”(等待时间可以接受)

这看起来很棒,它所玩的每个客户端都在移动他们的播放器,还有另一个播放器通过远程客户端移动

问题是球

在每个客户机上都有一个球,通过街机物理移动(在网中或在每个玩家的头部弹跳)。。。所以在很少的动作之后,因为球员的位置同步不完美,所以每个客户的球位置都不一样

您将如何实现球同步?

我在想一些选择:

  • 定期向服务器发送球位->服务器向客户端发送球位->客户端更新球位(使用一些插值)

  • 仅在一个客户端(主客户端)中启用球物理,然后定期将球位置从“主客户端”发送到“从客户端”(可能使用webRTC)

  • 重新开始,做一个真正的“权威服务器”,在服务器上安装Arcade物理(如果可能的话),在客户端安装


    • 你的问题让我想起了我过去的一个问题,那是个笑脸。我在网上做一个地图应用程序。所有客户端的映射对象必须相同(已同步)

      我解决这个问题的方法是将类库移动到服务器端,并使map对象成为单例对象。看看单例模式。单例对象不能实例化多次。我的意思是,每场比赛将只有一个球对象,客户端将使用它更新其本地对象

      以下是维基百科页面:

      在这之后,客户端要做的第一件事就是获取地图的最新实例(即您案例中的球),在服务器上进行修改和更新

      另一点是,多个客户端可能希望在sime时间更新服务器上的共享对象。这将导致一致性问题。许多实现包括对变量的锁定以限制访问。其他客户端等待锁释放和更新


      无论如何,在客户端拥有同一对象的多个实例不是一个好方法。

      要解决您的问题,您可以采用一种简单的方法, 你把玩家的攻击状态转移到服务器上, 现在,所有玩家从服务器订阅攻击信息,现在“客户”必须渲染子弹

      下面是一个小代码示例

      第1部分:[发布数据]

                  transferData = [
                  {
                      id: id,
                      name: Player.name,
                      position: Player.position,
                      facing: Player.facing,
                      hitFacing: Player.hitFacing,
                      health: Player.health,
                      energie: Player.energie,
                      healtbar: {width: Player.healthbar.width},
                      energiebar: {width: Player.energiebar.width},
                      isAttacking: Attack.isAttacking
                  }
              ];
      
              session.publish('org.example.character.data', transferData);
              Attack.isAttacking = false;
      
       // get player position
          session.subscribe('org.example.character.data',function (args) {
              var player = args[0];
              var exists = false;
              for (var i = 0; i < onlinePlayer.length; i++) {
      
                  if (onlinePlayer[i].uid == player.uid) {
                      var tmp = onlinePlayer[i];
                      player.sprite = tmp.sprite;
                      player.label = tmp.label;
                      player.status = tmp.status;
      
      
                      if (player.isAttacking && player.sprite != undefined) {
                          // HERE RENDER THE BALL
                          renderBall(player, this.game);
                      }
      
                      onlinePlayer[i] = player;
                      exists = true;
                  }
              }
      
              if (!exists)
                  onlinePlayer.push(player);
      
          }).then(
              function (sub) {
                  //console.log('subscribed to topic');
              },
              function (err) {
                  console.log('failed to subscribe to topic', err);
              }
          );
      
      第2部分:[订阅数据]

                  transferData = [
                  {
                      id: id,
                      name: Player.name,
                      position: Player.position,
                      facing: Player.facing,
                      hitFacing: Player.hitFacing,
                      health: Player.health,
                      energie: Player.energie,
                      healtbar: {width: Player.healthbar.width},
                      energiebar: {width: Player.energiebar.width},
                      isAttacking: Attack.isAttacking
                  }
              ];
      
              session.publish('org.example.character.data', transferData);
              Attack.isAttacking = false;
      
       // get player position
          session.subscribe('org.example.character.data',function (args) {
              var player = args[0];
              var exists = false;
              for (var i = 0; i < onlinePlayer.length; i++) {
      
                  if (onlinePlayer[i].uid == player.uid) {
                      var tmp = onlinePlayer[i];
                      player.sprite = tmp.sprite;
                      player.label = tmp.label;
                      player.status = tmp.status;
      
      
                      if (player.isAttacking && player.sprite != undefined) {
                          // HERE RENDER THE BALL
                          renderBall(player, this.game);
                      }
      
                      onlinePlayer[i] = player;
                      exists = true;
                  }
              }
      
              if (!exists)
                  onlinePlayer.push(player);
      
          }).then(
              function (sub) {
                  //console.log('subscribed to topic');
              },
              function (err) {
                  console.log('failed to subscribe to topic', err);
              }
          );
      
      //获取玩家位置
      session.subscribe('org.example.character.data',函数(args){
      var player=args[0];
      var=false;
      对于(变量i=0;i
      此示例适用于websocket服务器,例如在节点基础上带有autobahn.js的crossbar.io

      但您也可以使用其他服务器