连接到游戏的玩家不会在浏览器在线游戏中呈现

问题描述 投票:0回答:1

我正在学习如何使用 websocket 和 socket.io 库。我正在创建一个测试 2D 在线游戏。游戏中,用户控制一个球,通信方案如下:

客户端(game.js):

  • 向服务器发送消息
    "new player"
    ,如果有新玩家连接,则传输
    socket.id
  • 向服务器发送消息
    "movement"
    ,如果玩家按下/取消按下控制键,则发送
    { up: boolean, down: boolean, right: boolean, left: boolean, ID: socket.id }
    形式的按下按键的对象。
  • 收到消息时
    "state"
    应该渲染连接到游戏的所有玩家,但由于某种原因它不会发生(我在游戏中只看到自己)。

服务器(server.js):

  • 收到消息
    "new player"
    时,将连接的玩家的坐标存储在
    players
    对象中:
    [socket.id from the client]: {x: number, y: number}
  • 收到消息
    "movement"
    时,根据按下的按键改变
    players
    对象中玩家的坐标。
  • "state"
    毫秒向客户端发送一条
    16,67
    消息。

game.js
代码:

var socket = io();

socket.on("connect", function () {
  socket.emit("new player", socket.id);

  const canvas = document.getElementById("gameCanvas");
  const ctx = canvas.getContext("2d");

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

  const gridSize = 50;
  const playerSize = 20;
  const speed = 25;

  let playerX;
  let playerY;

  let movement = {
    up: false,
    down: false,
    left: false,
    right: false,
    shift: false,
    ID: socket.id
  };

  function drawGrid() {
    let colorIndex = 0;
    for (let x = 0; x < canvas.width; x += gridSize) {
      for (let y = 0; y < canvas.height; y += gridSize) {
        ctx.fillStyle = "green";
        ctx.fillRect(x, y, gridSize, gridSize);
      }
    }
  }

  function drawPlayer() {
    if (!playerX) return;
    if (!playerY) return;
    
    ctx.beginPath();
    ctx.arc(playerX, playerY, playerSize, 0, Math.PI * 2);
    ctx.fillStyle = "red";
    ctx.fill();
  }

  function clearCanvas() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  }

  function updateCamera() {
    if (!playerX) return;
    if (!playerY) return;
    const cameraX = canvas.width / 2 - playerX;
    const cameraY = canvas.height / 2 - playerY;
    ctx.translate(cameraX, cameraY);
  }

  function update() {
    clearCanvas();
    updateCamera();
    drawGrid();
    drawPlayer();
    ctx.setTransform(1, 0, 0, 1, 0, 0);
  }

  function handleKeyPress(event) {
    switch (event.key) {
      case "a":
        movement.left = true;
        break;
      case "d":
        movement.right = true;
        break;
      case "w":
        movement.up = true;
        break;
      case "s":
        movement.down = true;
        break;
      case "Shift":
        movement.shift = true;
        break;
    }
  }

  function handleKeyRelease(event) {
    switch (event.key) {
      case "a":
        movement.left = false;
        break;
      case "d":
        movement.right = false;
        break;
      case "w":
        movement.up = false;
        break;
      case "s":
        movement.down = false;
        break;
      case "Shift":
        movement.shift = false;
        break;
    }
  }

  window.addEventListener("keydown", handleKeyPress);
  window.addEventListener("keyup", handleKeyRelease);

  function gameLoop() {
    update();
    requestAnimationFrame(gameLoop);
    socket.emit("movement", movement);
  }

  window.addEventListener("resize", () => {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    update();
  });

  gameLoop();

  socket.on("state", function (players) {
    clearCanvas();

    playerX = players[socket.id]?.x;
    playerY = players[socket.id]?.y;
    ctx.fillStyle = "yellow";

    for (var id in players) {
      if (id === socket.id) continue;
      var player = players[id];
      ctx.beginPath();
      ctx.arc(player.x, player.y, playerSize, 0, 2 * Math.PI);
      ctx.fill();
    }
  });
});

server.js
代码:

let express = require('express');
let http = require('http');
let path = require('path');
let socketIO = require('socket.io');
let app = express();
let server = http.Server(app);
let io = socketIO(server);
let port = 5000;

app.set('port', port);
app.use('/static', express.static(__dirname + '/static'));

app.get('/', function (request, response) {
    response.sendFile(path.join(__dirname, 'index.html'));
});

server.listen(port, function () {
    console.log('Starting server on port: ' + port);
});

var players = {};
io.on('connection', function (socket) {
    socket.on('new player', function (ID) {
        players[ID] = {
            x: 959,
            y: 496.5
        };
    });
    socket.on('movement', function (data) {
        var player = players[data.ID] || {};
        if (data.left) {
            if (data.shift) {
                player.x -= 7.5
            } else {
                player.x -= 15;
            }
        }
        if (data.up) {
            player.y -= 15;
        }
        if (data.right) {
            if (data.shift) {
                player.x += 7.5
            } else {
                player.x += 15;
            }
        }
        if (data.down) {
            player.y += 15;
        }
    });
});
setInterval(function () {
    io.sockets.emit('state', players);
}, 1000 / 60);

index.html
代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Canvas Game</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
            background-color: #f0f0f0;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas"></canvas>
    <script src="/socket.io/socket.io.js"></script>
    <script src="static/game.js"></script>
</body>
</html>

我不明白问题到底是什么。我认为我已经正确配置了 websocket 通信。但是,当几个人进入游戏时,由于某种原因,他们在游戏中看不到对方。

游戏预览(红球为玩家):

Image

javascript node.js websocket socket.io 2d-games
1个回答
0
投票

您的主

requestAnimationFrame
循环会擦拭画布并仅绘制一名玩家:

function update() {
  clearCanvas();
  updateCamera();
  drawGrid();
  drawPlayer();
  ctx.setTransform(1, 0, 0, 1, 0, 0);
}

每秒运行约 60 次,立即擦除您在

socket.on("state", function (players) {
中所做的绘图。

在大多数动画中,将所有移动和渲染保留在更新循环中,以便您可以控制顺序。将渲染代码移出循环意味着它必须与主循环争夺优先级。

这是最直接的解决方法:

let players;
function update() {
  clearCanvas();
  updateCamera();
  drawGrid();
  drawPlayer();

  ctx.fillStyle = "yellow";
  for (var id in players) {
    if (id === socket.id) continue;
    var player = players[id];
    ctx.beginPath();
    ctx.arc(player.x, player.y, playerSize, 0, 2 * Math.PI);
    ctx.closePath();
    ctx.fill();
  }

  ctx.setTransform(1, 0, 0, 1, 0, 0);
}

// ...

socket.on("state", function (_players) {
  playerX = _players[socket.id]?.x;
  playerY = _players[socket.id]?.y;
  players = _players;
});

可能还有其他问题,总体设计还有改进的空间,但为了让您在眼前的问题上解开障碍,我就到此为止。

© www.soinside.com 2019 - 2024. All rights reserved.