liabru / matter-js

a 2D rigid body physics engine for the web ▲● ■
MIT License
16.62k stars 1.96k forks source link

Use in server side apps #101

Closed mattgrayisok closed 7 years ago

mattgrayisok commented 9 years ago

I'm using (and really enjoying) matter-js as a physics engine in a MMO game with an authoritative server architecture. To achieve this I need to run the physics simulation for the world on the server as well as the clients.

At present matter-js assumes the presence of a document object which doesn't exist within node.

To fix, I have included an extra bool option for the engine called createRenderer then wrapped the Render.create() call in a check for this property. There were also a couple of other places I needed to wrap, or return a function immediately, but once that was done everything worked perfectly.

Not sure if my solution is the best way to achieve this behaviour, but it would be a nice addition for scenarios such as mine.

Aralun commented 9 years ago

I also was interested in such a thing, but I was afraid MatterJS would be way too greedy on performances. I'm waiting for feedback on this matter!

mattgrayisok commented 9 years ago

In my setup I seem to be all good so far. My physics clock is running at 15ms per tick (with drift adjustments per tick to keep clients and server in sync) and it has no trouble simulating collision detection and some ray casting (people shooting each other) on 7 - 8 simultaneous players. This is running in a VM on my laptop with an i7 CPU, so definitely not the only process running.

That's as far as I've tested so far. Once the game is a little further developed I'll do some stress testing and try to find any bottlenecks. Will keep you updated.

liabru commented 9 years ago

@slice-beans

At present matter-js assumes the presence of a document object which doesn't exist within node.

Which version? Try with the latest master build if you're not already, you should be able to avoid passing any elements to the engine. Also check out the browserify branch.

Do you plan to release your game? Would be cool to see it!

@Aralun

I also was interested in such a thing, but I was afraid MatterJS would be way too greedy on performances.

The two major performance bottlenecks are the number of simultaneously colliding non-static bodies and rendering.

If you have no renderer in node then you never hit that bottleneck. If you enable sleeping you should be able to handle hundreds (thousands probably) of bodies quite easily I'd guess. I should probably do some benchmarks.

mattgrayisok commented 9 years ago

@liabru

I started by using the build provided by npm then moved over to pulling from this git repo directly, pulling the master branch and updated package.json to use matter.js rather than the 0.8.0 build so I think I'm on the latest version. That's the version that I couldn't get working initially so created a fork and made the updates mentioned above.

I haven't tried out the browserify branch yet, although I did see issue #80 and was waiting for a resolution to that. Has that been solved now?

The game is OS, you can see it here: https://github.com/slice-beans/wulfram-2d. It'll be a top down port of a game I used to play called Wulfram 2, hence the name! See the readme for things that have been implemented. Once I've got a few more features added I'll be sticking it on a server for testing so I'll send you over a link when I get to that stage.

liabru commented 9 years ago

I've just committed a fix for this. Please try out the latest master build.

You can do this on nodejs by reinstalling the matter-js package and then referencing the edge build like so:

var Matter = require('matter-js/build/matter.js');

This will be required for now until the next release. Note that Matter.Runner is not yet supported in nodejs, so you'll need your own game loop.

Here's an example:

var Matter = require('matter-js/build/matter.js');

var engine = Matter.Engine.create();

var boxA = Matter.Bodies.rectangle(400, 200, 80, 80);
var boxB = Matter.Bodies.rectangle(450, 50, 80, 80);
var ground = Matter.Bodies.rectangle(400, 610, 810, 60, { isStatic: true });

Matter.World.add(engine.world, [boxA, boxB, ground]);

console.log('boxA', boxA.position);
console.log('boxB', boxB.position);

for (var i = 0; i < 100; i++) {
    Matter.Events.trigger(engine, 'tick', { timestamp: engine.timing.timestamp });
    Matter.Engine.update(engine, engine.timing.delta);
    Matter.Events.trigger(engine, 'afterTick', { timestamp: engine.timing.timestamp });
}

console.log('boxA', boxA.position);
console.log('boxB', boxB.position);

Can you confirm this works for you?

liabru commented 9 years ago

The browserify branch has merged into master.

I'm also using the standalone build option which outputs a UMD module so it should work everywhere and I've updated the main field in package.json so that it points to the latest edge build. This means installing via npm should also work correctly now.

Thanks @slice-beans for your help. Please update thread #80 if you run into any issues!

glopratchet commented 9 years ago

i just did an install from NPM. after using the code block from above, node is asking me for a window. any thoughts on how i can get this to work. thank you

liabru commented 9 years ago

@glopratchet can you provide a stack trace please?

ken-blocklevel commented 9 years ago

@liabru I have the same issue: using var Matter = require('matter-js/build/matter.js');

Starting child process with 'node app.js' /Users/Ken/Blocklevel/Fussball/node_modules/matter-js/build/matter.js:3605 var _requestAnimationFrame = window.requestAnimationFrame || window.webkit ^ ReferenceError: window is not defined at /Users/Ken/Blocklevel/Fussball/node_modules/matter-js/build/matter.js:3605:34 at /Users/Ken/Blocklevel/Fussball/node_modules/matter-js/build/matter.js:4250:3 at Object. (/Users/Ken/Blocklevel/Fussball/node_modules/matter-js/build/matter.js:7376:3) at Module._compile (module.js:460:26) at Object.Module._extensions..js (module.js:478:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Module.require (module.js:365:17) at require (module.js:384:17) at Object. (/Users/Ken/Blocklevel/Fussball/entities/Engine.js:5:14) Program node app.js exited with code 1

using var Matter = rquire('matter-js');

Starting child process with 'node app.js' /Users/Ken/Blocklevel/Fussball/node_modules/matter-js/build/matter-0.8.0.min.js:7 {return o._nextId++}}();var p={};!function(){var a=60,e=a,h=1e3/a,j=window.req ^ ReferenceError: window is not defined at /Users/Ken/Blocklevel/Fussball/node_modules/matter-js/build/matter-0.8.0.min.js:7:22846 at /Users/Ken/Blocklevel/Fussball/node_modules/matter-js/build/matter-0.8.0.min.js:7:26335 at Object. (/Users/Ken/Blocklevel/Fussball/node_modules/matter-js/build/matter-0.8.0.min.js:8:22011) at Module._compile (module.js:460:26) at Object.Module._extensions..js (module.js:478:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Module.require (module.js:365:17) at require (module.js:384:17) at Object. (/Users/Ken/Blocklevel/Fussball/entities/Engine.js:5:14) Program node app.js exited with code 1

Aralun commented 9 years ago

Obviously the client-side code ended up on the server, which causes bad things with globals.

tolgair commented 8 years ago

@liabru matter.js is great. Thanks for your work.

i get the same error as ken-blocklevel as i used your sample code. I am trying to make multiplayer game using node.js. Can you please help me on hoe i can retrieve velocities and positions of object without rendering?

coryleeio commented 8 years ago

I was running into this same issue. using var Matter = require('matter-js/build/matter.js'); Did not work for me either, I got the same error as @ken-blocklevel

I downloaded code to fix it, and after building manually I found that the version generated by the build already contains the fix. So @liabru, you may just need to update the build version in the repo to fix his issue. If you are not ready to cut a new release yet.

@ken-blocklevel in the meantime, just build the project yourself, and import the ./build/matter.js into your project. Worked for me. =]

coryleeio commented 8 years ago

Thanks for the awesome library btw. =]

grant commented 8 years ago

This is what I'm using in the meantime. It's quite ugly but it works.

global.document = {
  createElement: function(){
    // Canvas
    return {
      getContext: function() {
        return {};
      }
    };
  }
};
global.window = {};
var Matter = require('matter-js/build/matter.js');
var World = Matter.World;
var Body = Matter.Body;
var Bodies = Matter.Bodies;
var Engine = Matter.Engine;

var options = {
  render: {
    element: null,
    controller: {
      create: function() {},
      clear: function() {},
      world: function() {}
    }
  },
  input: {
    mouse: {}
  }
};
var engine = Engine.create(options);
liabru commented 8 years ago

The npm package has been updated, see #170

coryleeio commented 8 years ago

Thanks!

danneu commented 8 years ago

What's the proper way to get an Engine running on Node in the latest release, v0.10.0?

Engine.run fails since Runner appears to be tied to the DOM. I did get the engine loop running using the manual setInterval that was posted earlier in this thread, but that post is a year old now.

liabru commented 8 years ago

By tied to the DOM do you mean it uses requestAnimationFrame? If you're running in node you should either update the engine yourself like so:

Engine.update(engine, 1000 / 60);

Or if you want to use Matter.Runner then you will need to tick it yourself in your own loop:

Runner.tick(runner, engine, time);

I guess it might be a nice idea to make Runner.run support node though, I think I'd just need to shim requestAnimationFrame with a node equivalent like setTimeout. Though I'm not sure how useful this is for server-side physics as you don't normally run them in realtime like you do in the browser.

danneu commented 8 years ago

@liabru Thanks, I see.

It would be helpful to have a quick blurb about Node in the README.md, perhaps a subheader in the Usage section, just to steer people in the right direction and satisfy the ctrl-f for "node".

It might seem obvious that you'd update the engine yourself in a loop on the server, but without reassurance in the readme/wiki, I first wondered if it was even Node-friendly to begin with.

And then I wondered if the trigger/update/trigger cycle as seen in the post from 2015 was still an up-to-date solution: https://github.com/liabru/matter-js/issues/101#issuecomment-116822596.

liabru commented 8 years ago

You're right, I'll add this information to the readme shortly 👍

liabru commented 7 years ago

Not exactly 'shortly'... but I've added a mention of support for Node.js in the readme!

souenzzo commented 6 years ago

Hello. I'm trying to run the engine "headless".

I folowed this thread and with that code I came up with an unexpected result: without the render collisions do not work and my boxes fall forever

var Matter = require('matter-js/build/matter.js');
var engine = Matter.Engine.create();
var boxA = Matter.Bodies.rectangle(400, 200, 80, 80);
var boxB = Matter.Bodies.rectangle(450, 50, 80, 80);
var ground = Matter.Bodies.rectangle(400, 610, 810, 60, { isStatic: true });
Matter.World.add(engine.world, [boxA, boxB, ground]);
console.log('boxA', boxA.position);
console.log('boxB', boxB.position);
Matter.Engine.update(engine, 10 * 1000);
console.log('boxA', boxA.position);
//With render I get: {x: 392.95126744647774, y: 539.9193206658802}
//Running headless "Engine.update(engine, 10* 1000)" I get: {x: 400, y: 86003.16900000001}
console.log('boxB', boxB.position);
//With render I get: {x: 492.47667249176163, y: 540.009431356056}
//Running headless "Engine.update(engine, 10* 1000)" I get: {x: 450, y: 85853.16900000001}

I tested headless runnig both on browser(ff) and nodejs(v10.3.0) with the same result: my boxes fall forever. I'm using 0.12.0-14 for browser and 0.14.1 for node (on browser I use via cljsjs project)

EDIT: I Will open a new issue

tacoe commented 6 years ago

Same thing here — this bit just makes the object fall through the static floor.

const Matter = require('matter-js');

const engine = Matter.Engine.create();

var world = engine.world;
var thing = Matter.Bodies.circle(0, 5, 0.5);
var floor = Matter.Bodies.rectangle(-100, 10, 200, 1, { isStatic: true });
Matter.World.add(world, [thing, floor]);

module.exports = function() {
  Matter.Engine.update(engine, 1000/60);

  console.log('thing', thing.position);
  console.log('floor', floor.position);
}