在 Node 服务器上运行 Matter.js [英] Running Matter.js on a Node server

查看:29
本文介绍了在 Node 服务器上运行 Matter.js的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to run a server that runs a physics simulation on a server and have clients connect to this server via websockets/socket.io. I know I can calculate the Engine separately from the rendering with Matter.js. So my question is, how do I get the engine data to the client?

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

I have a Game class, and on socket connection, I want to emit the World to the client to render.

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

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

在游戏状态下,我真的不确定要传递什么:

In the game state, I'm not really sure what to pass:

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 时,我只是使应用程序崩溃.它说已超出最大调用堆栈大小.

When I try pass self, or self.engine, or self.world, I just crash the app. It says Maximum call stack size exceeded.

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

What data do I need to extract from self.engine to send to the client neatly over WebSockets?

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

I know I need position data of the bodies. But even when I try

return self.engine.world.bodies;

然后它也会崩溃.

如何在不崩溃或不超过堆栈大小的情况下简单地将引擎/世界提供给客户端?

How can I simply get the engine/world to the clients without crashing or exceeding stack size?

推荐答案

如果服务器负责运行 matter.js 引擎,那么将 matter.js 对象从服务器发送到客户端可能是不必要的,而且成本高得令人望而却步.

If the server is responsible for running the matter.js engine, it's likely unnecessary and prohibitively expensive to send matter.js objects from the server to clients.

>

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

One possible design is for the server to emit a minimum amount of serialized information needed by the clients to render the game state on each tick. This is application-specific, but probably boils down to picking vertices from your mjs bodies and informing the clients of player positions, moves, scores, etc.

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

Once the client receives state, they are responsible for rendering it. This could be done with matter.js, canvas, p5.js, plain HTML or anything else. Clients are also responsible for reporting game-relevant mouse and keyboard actions to the server to be used by the game engine logic.

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

Here's a minimal, complete example of how this might work:

{
  "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>

现场演示项目代码出现故障.

存在其他方法,将某些引擎逻辑转移到客户端可能具有优势,因此请将此视为概念证明.有关其他设计建议,请参阅这篇文章.

Other approaches exist and there can be advantages to shifting some of the engine logic to clients, so consider this a proof of concept. See this post for additional design suggestions.

这篇关于在 Node 服务器上运行 Matter.js的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆