javascript - 使用套接字服务器绘制其他玩家的 Canvas 的奇怪行为

标签 javascript node.js html canvas socket.io

我正在尝试学习如何使用 socket.io 创建多人游戏。我目前有一个服务器存储所有玩家的位置并向每个玩家发送他们的位置,然后向玩家发送其他人的位置。据我所知,我得到的这些数字是完美的。我已经交叉检查了每个地点,它们都像应该的那样。但出于某种原因,我注意到当将玩家绘制到 Canvas 上时,我会出现一些奇怪的行为。以下是所发生情况的一些示例。左上角是玩家的坐标,下面的数字是其他玩家的坐标: enter image description here 从这张照片中你可以看到我只能看到其中一名球员。玩家位于左下角。这本身就很好。其他玩家可能距离较远,而不是在屏幕上。

enter image description here 在这张图片中,您可以看到与第一张图片相同的东西,但还有另一个立方体。现在这毫无意义了。立方体应该是左下方的立方体,并且 View 应该向下移动以适应立方体的 y 较低。

enter image description here 现在这张照片对我来说毫无意义。您只能看到最后一个立方体,并且当该立方体应该是最后一个立方体时,蓝色立方体仍然显示在最后一个立方体的上方和左侧。

这是我正在使用的代码。这对您来说可能没有任何意义,因为这是我的第一次尝试,需要重大改进。这是服务器的代码:

var express = require('express');
var app = express();
var serv = require('http').Server(app);
var speed = 10

app.get('/', function(req, res) {
  res.sendFile(__dirname + "/client/index.html");
});
app.use('client', express.static(__dirname + '/client'))
app.use(express.static(__dirname + '/client'));
serv.listen(2000);

characterList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", ".", ",", ":", ";", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "_", "[", "]", "(", ")", "<", ">", "|"]
var players = []
var connections = []
var io = require('socket.io')(serv, {});
io.sockets.on('connection', function(socket) {
  var address = socket.request.connection.remoteAddress;
  players.push({
    position: {
      x: Math.round(Math.random() * 1000),
      y: Math.round(Math.random() * 1000)
    }
  })
  connections.push(socket.id)
  socket.on('request', function(data) {
    length = data.length
    if (data.request == 'sessionID') {
      socket.emit('sendSessionID', {
        id: socket.id
      });
    }
    if (data.request == 'players') {
      var otherPlayers = [];
      for (var i = 0; i < players.length; i++) {
        if (connections[i] != data.id) {
          otherPlayers.push(players[i])
        } else {
          socket.emit('packet', {
            packetType: 'self',
            player: players[i]
          });
          console.log[i]
        }
      }
      socket.emit('sendPlayers', {
        Players: otherPlayers
      });
    }

  });

  socket.on('moveEvent', function(data) {
    var player = players[find(connections, data.id)]
    if (data.direction == "x+") {
      player.position.x = player.position.x + speed
    }
    if (data.direction == "y+") {
      player.position.y = player.position.y + speed
    }
    if (data.direction == "x-") {
      player.position.x = player.position.x - speed
    }
    if (data.direction == "y-") {
      player.position.y = player.position.y - speed
    }
  });

  function find(arrayName, string) {
    for (var i = 0; i < arrayName.length; i++) {
      if (string == arrayName[i]) {
        return i;
      }
    }
    return null;
  }

  function findKey(key) {
    for (var i = 0; i < arrayName.length; i++) {
      if (string == arrayName[i].ip) {
        return i;
      }
    }
    return null;
  }
  socket.on('disconnect', function() {
    var address = socket.request.connection.remoteAddress;
    players.splice(find(connections, this.id), 1)
    connections.splice(find(connections, this.id), 1)
  });
});

以下是客户端在屏幕上使用单个 Canvas 元素的代码:

$(function() {
  var socket = io();
  var sesID = ""
  var players = []
  var player = {}
  var game = document.getElementById('game')
  var gamectx = game.getContext('2d');
  var GameY = 0;
  var GameX = 0;
  var w = false;
  var a = false;
  var s = false;
  var d = false;
  socket.emit('request', {
    request: "sessionID"
  });

  socket.on('sendSessionID', function(data) {
    sesID = data.id
  });

  socket.on('sendPlayers', function(data) {
    players = data.Players
  });
  socket.on('packet', function(data) {
    if (data.packetType == 'self') {
      GameX = data.player.position.x;
      GameY = data.player.position.y;
      player = data.player;
    }
  });
  setInterval(function() {
    console.log(players.length)
    if (w == true) {
      socket.emit('moveEvent', {
        direction: "y+",
        id: sesID
      })
    }
    if (a == true) {
      socket.emit('moveEvent', {
        direction: "x+",
        id: sesID
      })
    }
    if (s == true) {
      socket.emit('moveEvent', {
        direction: "y-",
        id: sesID
      })
    }
    if (d == true) {
      socket.emit('moveEvent', {
        direction: "x-",
        id: sesID
      })
    }
    gamectx.canvas.width = window.innerWidth;
    gamectx.canvas.height = window.innerHeight;
    gamectx.clearRect(0, 0, game.width, game.height);
    socket.emit('request', {
      request: "players",
      id: sesID
    });
    for (var i = 0; i < players.length; i++) {
      gamectx.fillStyle = "#ff0000"
      gamectx.fillRect(players[i].position.x + GameX, players[i].position.y + GameY, 100, 100)
      console.log('drew player at ', players[i].position.x + GameX, players[i].position.y + GameY, 100, 100)
    }
    gamectx.fillStyle = "#0000ff"
    gamectx.fillRect(window.innerWidth / 2 - 50, window.innerHeight / 2 - 50, 100, 100);
    gamectx.font = "48px sans-serif";
    gamectx.strokeText("x: " + GameX + ", y: " + GameY, 10, 50);
    var playersList = ""
    for (var i = 0; i < players.length; i++) {
      playersList += "x: " + players[i].position.x + ", y: " + players[i].position.y + ", "
    }
    gamectx.font = "30px sans-serif";
    gamectx.strokeText(playersList, 10, 100);
  }, 30);

  $(document).keydown(function(e) {
    if (e.which == 65) {
      a = true;
    }
    if (e.which == 68) {
      d = true;
    }
    if (e.which == 87) {
      w = true;
    }
    if (e.which == 83) {
      s = true
    }
  });
  $(document).keyup(function(e) {
    console.log(e.which)
    if (e.which == 65) {
      a = false;
    }
    if (e.which == 68) {
      d = false;
    }
    if (e.which == 87) {
      w = false;
    }
    if (e.which == 83) {
      s = false
    }
  });
});

如果需要,这里是 html 代码:

<html>
<div id="location-div">
</div>
<canvas id="game"></canvas>

<head>
  <script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
  <script src="js/jquery-3.1.1.min.js"></script>
  <script src="js/game.js"></script>
  <link rel="stylesheet" href="css/index.css">
</head>

<body>
</body>

</html>

如果这对任何人都有意义,请提前感谢您的帮助!

最佳答案

一切都是相对的。

在一维 x 和 Canvas 中心 cc = 100 中。

你绘制玩家gameX + x你在cc==100绘制self所以如果self是150并且玩家是200你在150+200绘制玩家=350 但 self 是在 cc 处绘制的,使得玩家位于 self 右侧 350-cc = 250 像素。播放器应该位于自身 canvas.width/2 + 50 右侧 50 像素处。

修复在 Canvas 中心(cc或原点)绘制玩家以及自身和玩家之间的差异。

ctx.drawRect((canvas.width / 2) + (player.x - self.x), ...

顺便说一句

制作字符数组

charArr = "0123456789abcdefghijklmnopqrstuvwxyz.,:;!@#$%^&*()-_[]()<>|".split("");

下次需要这样的数组时可以节省大量的输入。

永远不要依赖数据连续性

仅发送运动并依赖每个客户端保持正确的位置是行不通的,一台机器上错过一个数据包,每个人都会有错误的 View 。发送时间(带时间戳)的绝对位置加上当前增量。不是作为字符串,而是编码成可以发送的最小数据包。

如果下一个数据包迟到,则使用玩家的最后一个增量来更新位置。当数据包到达时恢复到实际位置。如果您迟到丢失数据包,则允许交互回滚。也就是说,如果位置是从之前的增量中猜测出来的,并且新的位置证明命中是失误,则不要杀死玩家。

现在是什么意思?

请记住,光速是有限的,您以 30 毫秒(3/100 秒)更新,这给光(@~300,000,000mps)时间移动 9,000,000m 或 9000km,刚好有足够的时间穿越一个国家并返回,添加数据包交换、客户端处理、响应传输延迟等,对于世界范围的游戏,预计最小 ping 时间为 150ms 以上(或仅将游戏限制为低 ping(本地))我们现在都有不同的情况,请考虑到这一点,编写多人游戏。

关于javascript - 使用套接字服务器绘制其他玩家的 Canvas 的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39951170/

相关文章:

node.js - 检索node.js child_process的结果

javascript - 如何将此 Node 代码分解为不同的文件

javascript - 如何循环获取 Amazon S3 对象?

javascript - 检索谷歌地图的坐标

javascript - 设置a-tag显示: block but initially hidden

javascript - 将 PHP 添加到 JS 日历

html - 从选择元素中删除滚动条

html - 循环内容中的行不向上移动

javascript - Angular $watch 不起作用,除非我点击屏幕上的一些

javascript - 用 SVG 图形替换 mat-icon