Gugis Posted November 9, 2013 Share Posted November 9, 2013 I run identical simulation on box2dweb and box2dnode (https://github.com/M0rph3v5/box2dnode), but simulation on node.js runs longer (sometimes 5 seconds, sometimes 10 seconds longer). How to sync box2d on browser and on node.js to make simulations end about the same time? Quote Link to comment Share on other sites More sharing options...
Psychho Posted November 9, 2013 Share Posted November 9, 2013 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? Quote Link to comment Share on other sites More sharing options...
Gugis Posted November 11, 2013 Author Share Posted November 11, 2013 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. Quote Link to comment Share on other sites More sharing options...
Psychho Posted November 11, 2013 Share Posted November 11, 2013 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 secondIf 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. Quote Link to comment Share on other sites More sharing options...
Gugis Posted November 12, 2013 Author Share Posted November 12, 2013 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 secondIf 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. Quote Link to comment Share on other sites More sharing options...
Gugis Posted November 16, 2013 Author Share Posted November 16, 2013 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); }})(); Quote Link to comment Share on other sites More sharing options...
Quetzacotl Posted November 18, 2013 Share Posted November 18, 2013 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 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.