Jump to content

Box2DWeb and Box2DNode synchronization


Gugis
 Share

Recommended Posts

What do you mean by 'simulation on node.js runs longer'? Are you only simulating for a certain amount of time or what are you trying to achieve?

Let's say I create bodies on box2dweb and box2dnode with same parameters. Then I apply impulse on them.

Body on box2dweb stops moving 5 seconds later, but body on box2dnode stops 9 seconds later since applying impulse.

Link to comment
Share on other sites

Let's say I create bodies on box2dweb and box2dnode with same parameters. Then I apply impulse on them.

Body on box2dweb stops moving 5 seconds later, but body on box2dnode stops 9 seconds later since applying impulse.

First of all in the example its simulating 2 seconds every 1 second.

function update() {    world.Step(1/30, 10, 10);//Simulate the world for 1/30th of a second    console.log(body.GetPosition());}setInterval(update, 1000/60);//Call update every 1/60th of a second

If you look you are simulating the world for twice as long as you should, but fixing this wont fix you're problem.

 

 

Here's a fix using delta time to simulate at a fixed rate:

function update() {    nextTime = Date.now();    deltaTime = (nextTime-lastTime)/1000;    lastTime = nextTime;    world.Step(deltaTime, 10, 10);    console.log(body.GetPosition());    setTimeout(update,0);}var lastTime,nextTime,deltaTime;lastTime = Date.now();update();

This will fix most of your problem, but you will still need to add in some sort of latency compensation if you want the simulations to start at the same time.

Link to comment
Share on other sites

First of all in the example its simulating 2 seconds every 1 second.

function update() {    world.Step(1/30, 10, 10);//Simulate the world for 1/30th of a second    console.log(body.GetPosition());}setInterval(update, 1000/60);//Call update every 1/60th of a second

If you look you are simulating the world for twice as long as you should, but fixing this wont fix you're problem.

 

 

Here's a fix using delta time to simulate at a fixed rate:

function update() {    nextTime = Date.now();    deltaTime = (nextTime-lastTime)/1000;    lastTime = nextTime;    world.Step(deltaTime, 10, 10);    console.log(body.GetPosition());    setTimeout(update,0);}var lastTime,nextTime,deltaTime;lastTime = Date.now();update();

This will fix most of your problem, but you will still need to add in some sort of latency compensation if you want the simulations to start at the same time.

 

 

Thanks, I will try this.

Link to comment
Share on other sites

Now both simulations stops simultaneously, but bodies positions are different. You can test it here: http://78.62.160.169/billiard/

 

Here's Box2Dweb code:

	var   b2Vec2 = Box2D.Common.Math.b2Vec2		,  b2AABB = Box2D.Collision.b2AABB		,	b2BodyDef = Box2D.Dynamics.b2BodyDef		,	b2Body = Box2D.Dynamics.b2Body		,	b2FixtureDef = Box2D.Dynamics.b2FixtureDef		,	b2Fixture = Box2D.Dynamics.b2Fixture		,	b2World = Box2D.Dynamics.b2World		,	b2MassData = Box2D.Collision.Shapes.b2MassData		,	b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape		,	b2CircleShape = Box2D.Collision.Shapes.b2CircleShape		,	b2DebugDraw = Box2D.Dynamics.b2DebugDraw		,  b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef		, b2ContactListener = Box2D.Dynamics.b2ContactListener;		;var canvas;var world;var balls = [];var add = [];var remove = [];var lastTime,nextTime,deltaTime;window.onload = function(){    canvas = document.getElementById("canvas");    socket = io.connect('http://78.62.160.169:8000');       build();    debug();    socket.on('turn', function(data){        if(!isEmpty(balls)){            for(var ball in balls){                console.log({name: ball, x: balls[ball].GetBody().GetPosition().x, y: balls[ball].GetBody().GetPosition().y});                remove[ball] = balls[ball].GetBody().GetUserData().name;            }              }        for(var ball in data.balls){            add[ball] = data.balls[ball];        }        canvas.addEventListener("click", mouseClick, true);	    });        socket.on('shoot', shoot);}function addBall(name, x, y){    var ballFix = new b2FixtureDef;    ballFix.density = 1.0;    ballFix.friction = 0.5;    ballFix.restitution = 0.4;    var ballBody = new b2BodyDef;    ballBody.type = b2Body.b2_dynamicBody;    ballFix.shape = new b2CircleShape(0.45);    ballBody.linearDamping = 0.5;    ballBody.angularDamping = 5.0;			    ballBody.position.Set(x, y);    balls[name] = world.CreateBody(ballBody).CreateFixture(ballFix);			    balls[name].GetBody().SetUserData({name: name});}function removeBall(name) {    world.DestroyBody(balls[name].GetBody());    delete balls[name];};function build(){    world = new b2World(new b2Vec2(0, 0), true);		var bodyDef = new b2BodyDef;		var bodyPoly = new b2PolygonShape;		var bodyFix = new b2FixtureDef;				var vertexArray = new Array();		bodyDef.type = b2Body.b2_staticBody;		 		bodyFix.density = 0.5;		bodyFix.friction = 0.0;		bodyFix.restitution = 0.5;		var ver1 = new b2Vec2( 0, 0);		var ver2 = new b2Vec2( 0.4, 0.4);		var ver3 = new b2Vec2( 0.4, 11.45);		var ver4 = new b2Vec2( 0, 11.8);		vertexArray.push(ver1, ver2, ver3, ver4);		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(1.35, 3.1);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2Vec2( 0, 0.4);		var ver2 = new b2Vec2( 0.4, 0.0);		var ver3 = new b2Vec2( 0.4, 11.7);		var ver4 = new b2Vec2( 0, 11.45);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(28.2, 3.05);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2Vec2( 0.0, 0.4);		var ver2 = new b2Vec2( 0.4, 0.0);		var ver3 = new b2Vec2( 11.8, 0.0);		var ver4 = new b2Vec2( 12.0, 0.4);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(2.3, 15.37);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2Vec2( 0.0, 0.4);		var ver2 = new b2Vec2( 0.3, 0.0);		var ver3 = new b2Vec2( 11.75, 0.0);		var ver4 = new b2Vec2( 12.08, 0.4);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(15.65, 15.37);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2Vec2( 0.4, 0.4);		var ver2 = new b2Vec2( 0.0, 0.0);		var ver3 = new b2Vec2( 12.05, 0.0);		var ver4 = new b2Vec2( 11.8, 0.4);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(2.3, 2.23);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2Vec2( 0.2, 0.4);		var ver2 = new b2Vec2( 0.0, 0.0);		var ver3 = new b2Vec2( 12.0, 0.0);		var ver4 = new b2Vec2( 11.6, 0.4);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(15.65, 2.23);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);}	function debug() {         var debugDraw = new b2DebugDraw();			debugDraw.SetSprite(canvas.getContext("2d"));			debugDraw.SetDrawScale(30.0);			debugDraw.SetFillAlpha(0.5);			debugDraw.SetLineThickness(1.0);			debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);			world.SetDebugDraw(debugDraw);			//window.setInterval(update, 1000 / 60);            lastTime = Date.now();            update();	}; function update2(){		if(!isEmpty(remove)){			for(var b in remove){				removeBall(remove[b]);			}		}		remove = [];				if(!isEmpty(add)){			for(var b in add){				addBall(b, add[b].x, add[b].y);			}		}		add = [];        world.Step(1 / 60, 10, 10);		world.DrawDebugData();		world.ClearForces();}function update() {    nextTime = Date.now();    deltaTime = (nextTime-lastTime)/1000;    lastTime = nextTime;		if(!isEmpty(remove)){			for(var b in remove){				removeBall(remove[b]);			}		}		remove = [];				if(!isEmpty(add)){			for(var b in add){				addBall(b, add[b].x, add[b].y);			}		}		add = [];        world.Step(deltaTime, 10, 10);    world.DrawDebugData();    world.ClearForces();    setTimeout(update,0);}function isEmpty(obj){    for(var i in obj){ return false;}    return true;}function mouseClick(e) {    var point = {x: (e.clientX-$('#canvas').offset().left)/30, y: (e.clientY-$('#canvas').offset().top)/30};    var pos = {x: balls['cue'].GetBody().GetPosition().x, y: balls['cue'].GetBody().GetPosition().y};    var power = {x: (point.x - pos.x)*10, y: (point.y - pos.y)*10};    var totalpower = Math.sqrt(Math.pow(Math.abs(power.x), 2) + Math.pow(Math.abs(power.y), 2));    canvas.removeEventListener("click", mouseClick, true);    socket.emit('shoot', {x: power.x, y: power.y});   };function shoot(data){    balls['cue'].GetBody().ApplyImpulse(new b2Vec2(data.x, data.y), new b2Vec2(0,0));   }

Box2dNode:

var b2d = require("box2dnode");var io = require('socket.io').listen(8000, {transports:['flashsocket', 'websocket', 'htmlfile', 'xhr-polling', 'jsonp-polling']});io.set('log level', 1);io.sockets.on('connection', function(socket){    socket.game = new Game(socket);    socket.on('shoot', function(data){		socket.game.shoot(data);	});});process.on('uncaughtException', function (err) {  console.error(err);  console.log("Node NOT Exiting...");});(function(){    Game = function(socket){         this.socket = socket;        this.world = new b2d.b2World(new b2d.b2Vec2(0, 0), true);        this.setupBorders();        this.setupBalls();        this.lastTime = Date.now();        this.update(this);        this.socket.emit('turn', {balls: this.positions()});    };            Game.prototype.update = function(self){        self.nextTime = Date.now();        self.deltaTime = (self.nextTime-self.lastTime)/1000;        self.lastTime = self.nextTime;        self.world.Step(self.deltaTime, 10, 10);        setTimeout(function(){ self.update(self) },0);        }			            Game.prototype.setupBorders = function(){		var bodyDef = new b2d.b2BodyDef;		var bodyPoly = new b2d.b2PolygonShape;		var bodyFix = new b2d.b2FixtureDef;				var vertexArray = new Array();		bodyDef.type = b2d.b2Body.b2_staticBody;		 		bodyFix.density = 0.5;		bodyFix.friction = 0.0;		bodyFix.restitution = 0.5;		var ver1 = new b2d.b2Vec2( 0, 0);		var ver2 = new b2d.b2Vec2( 0.4, 0.4);		var ver3 = new b2d.b2Vec2( 0.4, 11.45);		var ver4 = new b2d.b2Vec2( 0, 11.8);		vertexArray.push(ver1, ver2, ver3, ver4);		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(1.35, 3.1);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2d.b2Vec2( 0, 0.4);		var ver2 = new b2d.b2Vec2( 0.4, 0.0);		var ver3 = new b2d.b2Vec2( 0.4, 11.7);		var ver4 = new b2d.b2Vec2( 0, 11.45);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(28.2, 3.05);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2d.b2Vec2( 0.0, 0.4);		var ver2 = new b2d.b2Vec2( 0.4, 0.0);		var ver3 = new b2d.b2Vec2( 11.8, 0.0);		var ver4 = new b2d.b2Vec2( 12.0, 0.4);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(2.3, 15.37);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2d.b2Vec2( 0.0, 0.4);		var ver2 = new b2d.b2Vec2( 0.3, 0.0);		var ver3 = new b2d.b2Vec2( 11.75, 0.0);		var ver4 = new b2d.b2Vec2( 12.08, 0.4);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(15.65, 15.37);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2d.b2Vec2( 0.4, 0.4);		var ver2 = new b2d.b2Vec2( 0.0, 0.0);		var ver3 = new b2d.b2Vec2( 12.05, 0.0);		var ver4 = new b2d.b2Vec2( 11.8, 0.4);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(2.3, 2.23);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);		var ver1 = new b2d.b2Vec2( 0.2, 0.4);		var ver2 = new b2d.b2Vec2( 0.0, 0.0);		var ver3 = new b2d.b2Vec2( 12.0, 0.0);		var ver4 = new b2d.b2Vec2( 11.6, 0.4);		vertexArray = [ver1, ver2, ver3, ver4];		bodyPoly.SetAsArray(vertexArray, vertexArray.length);		bodyFix.shape = bodyPoly;		bodyDef.position.Set(15.65, 2.23);		this.world.CreateBody(bodyDef).CreateFixture(bodyFix);    };        Game.prototype.setupBalls = function(){        this.balls = [];                var ballFix = new b2d.b2FixtureDef;        ballFix.density = 1.0;        ballFix.friction = 0.5;        ballFix.restitution = 0.4;        var ballBody = new b2d.b2BodyDef;        ballBody.type = b2d.b2Body.b2_dynamicBody;        ballFix.shape = new b2d.b2CircleShape(0.45);        ballBody.linearDamping = 0.5;        ballBody.angularDamping = 5.0;			        ballBody.position.Set(8.42, 9);        this.balls['cue'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);			        this.balls['cue'].GetBody().SetUserData({name: 'cue'});			        ballBody.position.Set(21.60, 9);        this.balls['b1'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);	        this.balls['b1'].GetBody().SetUserData({name: 'b1'});			        ballBody.position.Set(22.43, 8.53);        this.balls['b2'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);	        this.balls['b2'].GetBody().SetUserData({name: 'b2'});			        ballBody.position.Set(22.43, 9.47);        this.balls['b3'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);        this.balls['b3'].GetBody().SetUserData({name: 'b3'});			        ballBody.position.Set(23.26, 8.07);        this.balls['b4'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);			        this.balls['b4'].GetBody().SetUserData({name: 'b4'});			        ballBody.position.Set(23.26, 9);        this.balls['b5'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);			        this.balls['b5'].GetBody().SetUserData({name: 'b5'});			        ballBody.position.Set(23.26, 9.94);        this.balls['b6'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);        this.balls['b6'].GetBody().SetUserData({name: 'b6'});						        ballBody.position.Set(24.09, 7.6);        this.balls['b7'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);	        this.balls['b7'].GetBody().SetUserData({name: 'b7'});			        ballBody.position.Set(24.09, 8.54);        this.balls['b8'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);			        this.balls['b8'].GetBody().SetUserData({name: 'b8'});			        ballBody.position.Set(24.09, 9.48);        this.balls['b9'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);	        this.balls['b9'].GetBody().SetUserData({name: 'b9'});			        ballBody.position.Set(24.09, 10.42);        this.balls['b10'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);        this.balls['b10'].GetBody().SetUserData({name: 'b10'});			        ballBody.position.Set(24.92, 7.13);        this.balls['b11'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);	        this.balls['b11'].GetBody().SetUserData({name: 'b11'});			        ballBody.position.Set(24.92, 8.07);        this.balls['b12'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);			        this.balls['b12'].GetBody().SetUserData({name: 'b12'});			        ballBody.position.Set(24.92, 9);        this.balls['b13'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);			        this.balls['b13'].GetBody().SetUserData({name: 'b13'});	        ballBody.position.Set(24.92, 9.94);        this.balls['b14'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);		        this.balls['b14'].GetBody().SetUserData({name: 'b14'});			        ballBody.position.Set(24.92, 10.88);        this.balls['b15'] = this.world.CreateBody(ballBody).CreateFixture(ballFix);        this.balls['b15'].GetBody().SetUserData({name: 'b15'});        };        Game.prototype.positions = function(){        var self = this;		var pos = {};		var i = 0;		if(!self.balls['cue']){			var ballFix = new b2d.b2FixtureDef;			ballFix.density = 1.0;			ballFix.friction = 0.5;			ballFix.restitution = 0.4;			var ballBody = new b2d.b2BodyDef;			ballBody.type = b2d.b2Body.b2_dynamicBody;			ballFix.shape = new b2d.b2CircleShape(0.45);			ballBody.linearDamping = 0.5;			ballBody.angularDamping = 5.0;						ballBody.position.Set(8.42, 9);			self.balls['cue'] = self.world.CreateBody(ballBody).CreateFixture(ballFix);						self.balls['cue'].GetBody().SetUserData({name: 'cue'});		}		for(var key in self.balls){			i++;			pos[key] = self.balls[key].GetBody().GetPosition();		}		return pos;    };        Game.prototype.shoot = function(data){        var self = this;        self.socket.emit('shoot', data);        self.balls['cue'].GetBody().ApplyImpulse(new b2d.b2Vec2(data.x, data.y), new b2d.b2Vec2(0,0));  			self.timer = setInterval(function(){                				var i = 0;				var j = 0;				for(var b in self.balls){					j++;					if(!self.balls[b].GetBody().IsAwake()){							i++;					}				}									if(i == j){					var pos = self.positions();					clearInterval(self.timer);					self.socket.emit('turn', {balls: pos});                    console.log(pos);				}			}, 500);    }})();
Link to comment
Share on other sites

Is Box2D deterministic?

For the same input, and same binary, Box2D will reproduce any simulation. Box2D does not use any random numbers nor base any computation on random events (such as timers, etc).

However, people often want more stringent determinism. People often want to know if Box2D can produce identical results on different binaries and on different platforms. The answer is no. The reason for this answer has to do with how floating point math is implemented in many compilers and processors. I recommend reading this article if you are curious: http://www.yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...