【问题标题】:Running Matter.js on a Node server在节点服务器上运行 Matter.js
【发布时间】:2020-07-24 14:51:22
【问题描述】:

我正在尝试运行一个在服务器上运行物理模拟的服务器,并让客户端通过 websockets/socket.io 连接到该服务器。我知道我可以使用 Matter.js 与渲染分开计算引擎。所以我的问题是,如何将引擎数据发送给客户端?

我有一个Game 类,在套接字连接上,我想将World 发送给客户端进行渲染。

var g = new Game()
g.initEngine()

io.sockets.on('connection', function(socket) {
   io.emit('gamestate', g.getGameState())
});

在游戏状态下,我不太确定要通过什么:

var Game = function(Player1, Player2) {
    var self = this
    this.gameID = 22
    this.engine = null
    this.world = null
    
    // Get game state - WHAT DO I SEND HERE!??
    this.getGameState = function() {
      return self.engine
    }

   // Create the engine
   this.initEngine = function() {
    self.engine = M.Engine.create();
    self.world = self.engine.world;
    self.world.gravity.x = 0;
    self.world.gravity.y = 0;
    M.Engine.update(self.engine, 122000 / 60);
  }
}

当我尝试通过selfself.engineself.world 时,我只是让应用程序崩溃。它说超出了最大调用堆栈大小。

我需要从self.engine 中提取哪些数据才能通过 WebSockets 整齐地发送到客户端?

我知道我需要尸体的位置数据。但即使我尝试

return self.engine.world.bodies;

它也会崩溃。

我怎样才能在不崩溃或超出堆栈大小的情况下简单地将引擎/世界提供给客户端?

【问题讨论】:

  • 为什么需要将引擎或世界数据传给客户端?它可能是特定于应用程序的,但理想情况下,您只需发送与客户端渲染和更新游戏状态所需的数据相对应的数据,例如身体顶点的 x/y。

标签: javascript socket.io matter.js


【解决方案1】:

如果服务器负责运行 matter.js 引擎,则可能没有必要将 matter.js 对象从服务器发送到客户端。

一种可能的设计是让服务器发出客户端所需的最少数量的序列化信息,以在每个滴答时呈现游戏状态。这是特定于应用程序的,但可能归结为从您的 mjs 主体中选择顶点并通知客户端玩家位置、移动、得分等。

一旦客户端收到状态,他们就负责渲染它。这可以使用 matter.js、canvas、p5.js、纯 HTML 或其他任何东西来完成。客户端还负责向服务器报告游戏相关的鼠标和键盘操作,以供游戏引擎逻辑使用。

这里有一个最小的、完整的例子来说明它是如何工作的:

package.json:

{
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "matter-js": "^0.16.1",
    "socketio": "^1.0.0"
  },
  "engines": {
    "node": "12.x"
  }
}

server.js:

const express = require("express");
const Matter = require("matter-js");

const app = express();
const http = require("http").createServer(app);
const io = require("socket.io").listen(http);

const frameRate = 1000 / 30;
const canvas = {width: 300, height: 200};
const boxes = 20
const boxSize = 20
const wallThickness = 20

const entities = {
  boxes: [...Array(boxes)].map(() => 
    Matter.Bodies.rectangle(
      Math.random() * canvas.width, 
      boxSize, 
      Math.random() * boxSize + boxSize,
      Math.random() * boxSize + boxSize,
    )
  ),
  walls: [
    Matter.Bodies.rectangle(
      canvas.width / 2, 0, 
      canvas.width, 
      wallThickness, 
      {isStatic: true}
    ),
    Matter.Bodies.rectangle(
      0, canvas.height / 2, 
      wallThickness, 
      canvas.height, 
      {isStatic: true}
    ),
    Matter.Bodies.rectangle(
      canvas.width, 
      canvas.width / 2, 
      wallThickness, 
      canvas.width, 
      {isStatic: true}
    ),
    Matter.Bodies.rectangle(
      canvas.width / 2, 
      canvas.height, 
      canvas.width, 
      wallThickness, 
      {isStatic: true}
    ),
  ]
};

const engine = Matter.Engine.create();
Matter.World.add(engine.world, [].concat(...Object.values(entities)));
const toVertices = e => e.vertices.map(({x, y}) => ({x, y}));

setInterval(() => {
  Matter.Engine.update(engine, frameRate);
  io.emit("update state", {
    boxes: entities.boxes.map(toVertices),
    walls: entities.walls.map(toVertices),
  });
}, frameRate);

io.on("connection", socket => {
  socket.on("register", cb => cb({canvas: canvas}));
  socket.on("player click", coordinates => {
    entities.boxes.forEach(box => {
      // https://stackoverflow.com/a/50472656/6243352
      const force = 0.01;
      const deltaVector = Matter.Vector.sub(box.position, coordinates);
      const normalizedDelta = Matter.Vector.normalise(deltaVector);
      const forceVector = Matter.Vector.mult(normalizedDelta, force);
      Matter.Body.applyForce(box, box.position, forceVector);
    });
  });
});

app.use(express.static("public"));

app.get("/", (req, res) => 
  res.sendFile(__dirname + "/views/index.html")
);

http.listen(process.env.PORT, () =>
  console.log("http listening on " + process.env.PORT)
);

index.html:

<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script>

  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  const ctx = canvas.getContext("2d");
  const socket = io();
  
  const draw = (body, ctx) => {
    ctx.beginPath();
    body.forEach(e => ctx.lineTo(e.x, e.y));
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
  };
  
  socket.once("connect", () => {
    socket.emit("register", res => {
      canvas.width = res.canvas.width;
      canvas.height = res.canvas.height;
    });
  });

  socket.on("update state", ({boxes, walls}) => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "#111";
    ctx.strokeStyle = "#111";
    walls.forEach(wall => draw(wall, ctx));
    ctx.fillStyle = "#aaa";
    boxes.forEach(box => draw(box, ctx));
  });
  
  document.addEventListener("mousedown", e => {
    socket.emit("player click", {x: e.offsetX, y: e.offsetY}); 
  });
</script>

Live demoproject code 出现故障。

存在其他方法,将一些引擎逻辑转移到客户端可能会有优势,因此请将此视为概念验证。有关其他设计建议,请参阅 this post

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-06
    • 1970-01-01
    • 1970-01-01
    • 2015-01-28
    • 2014-11-04
    • 2021-12-05
    相关资源
    最近更新 更多