HTML5: Combining Physics Engine (box2dWeb) With DeviceOrientation
Join the DZone community and get the full member experience.
Join For Freewhen the first smartphones came out, they all had the traditional labyrinth applications. application where you had to move a ball around a certain path by tilting and moving your phone around. there is also an html5 specification that allows you to do this directly from your browser. this would allow us to create all kind of cool games that run directly from the browser. in this article i'll show you my first experiments with this api in combination with a javascript based physics engine. the goal is to create a simple html5 application where you can move a number of balls around using the motion sensors in your device. what we're aiming for is this:
you can test this for yourself here: " box2dweb and deviceorientation demo ".
before i start with the explanation, a quick note on device compatibility. my initial idea was to write this for my tablet to play around a bit with the various sensors, html5 apis etc. that are available. the problem however is, that at the moment the box2dweb.js library is much too heavy for mobile development. i've alo tried other physics javascript libraries out there, but they all result in a framerate of about 2 to 3 frames per second. i'm currently looking into offloading the physics engine to a serverside backend (just as i did in the facedetection example). so i tested and changed this example to work with the sensors from my macbook pro.
accessing device sensors
accessing a device's sensors is very easy. the specification defines that you have to register for a specific event, and you'll receive a callback at a specific interval like this:
window.addeventlistener("deviceorientation", function(event) { // process event.alpha, event.beta and event.gamma }, true); </javscript> the event you receive contains the following information: <javascript> {alpha: 90, // represents position on the z-axis beta: 0, // represents position on the x-axis gamma: 0}; // represents position on the y-axis
for my macbook this results in the following set of events:
absolute: null alpha: null beta: 2.2814938370474303 bubbles: false cancelbubble: false cancelable: false clipboarddata: undefined currenttarget: domwindow defaultprevented: false eventphase: 0 gamma: 1.3697507397371704 returnvalue: true srcelement: domwindow target: domwindow timestamp: 1336114836578 type: "deviceorientation"
as you can see, besides a lot of other info, we receive an alpha, beta and a gamma. for my macbook i never receive an alpha value. for this demo i want to roll all the balls, from the image, to the left when i tilt my laptop to the left and they should roll to the right when i tilt my laptop to the right. the same goes for when i tilt my laptop backwards, the balls should roll to the top and when i tilt my laptop downwards the balls should roll to the bottom.
i use the following listener for this:
// initialize the device orientation and set the callback function initorientation() { if (window.deviceorientationevent) { console.log("deviceorientation is supported"); window.addeventlistener('deviceorientation', function (eventdata) { var lr = eventdata.gamma; // used as x gravity var fb = eventdata.beta; // used as y gravity var newxgravity = math.round(lr / 2); var newygravity = math.round(fb / 2); if (newxgravity != world.m_gravity.x || newygravity != world.m_gravity.y ) { // set new gravity world.m_gravity.x = newxgravity world.m_gravity.y = newygravity // wakeup all the bodies when the gravity changes for (var body = world.m_bodylist; body; body = body.m_next) { body.setawake(true); } } }, false); } else { alert("not supported"); } }
in this listing i start listening for the event and retrieve the new x and the new y gravity based on the tilt of the laptop. when the tilt has changed, i set the new gravity of the physics world and wake up any sleeping bodies (see below for more info on this). and that's all you need to do.
setup the physics engine
next lets look at the physics engine. i've use box2dweb , which is an javascript implementation of the box2d javascript engine. i won't go into too much detail here, but the comments in the code should explain what is happening.
function initworld() { 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; // setup the world world = new b2world( new b2vec2(0, 10) //gravity , true //allow sleep ); // define the borders var fixdef = new b2fixturedef; fixdef.density = 0.1; fixdef.friction = 0.3; fixdef.restitution = 0.2; var bodydef = new b2bodydef; //create enclosure bodydef.type = b2body.b2_staticbody; fixdef.shape = new b2polygonshape; fixdef.shape.setasbox(width / 10, 2); // draw lower bound bodydef.position.set(0, height / scale); world.createbody(bodydef).createfixture(fixdef); // draw upper bound bodydef.position.set(0, 0); world.createbody(bodydef).createfixture(fixdef); // draw left bound fixdef.shape.setasbox(2, height); bodydef.position.set(0, 0); world.createbody(bodydef).createfixture(fixdef); // draw right bound bodydef.position.set(width / scale, 0); world.createbody(bodydef).createfixture(fixdef); //create 100 objects bodydef.type = b2body.b2_dynamicbody; for (var i = 0; i < 100; ++i) { fixdef.shape = new b2circleshape(1); bodydef.position.x = (math.random() * width) / scale; bodydef.position.y = (math.random() * height) / scale; world.createbody(bodydef).createfixture(fixdef); } //setup debug draw var debugdraw = new b2debugdraw(); debugdraw.setsprite(document.getelementbyid("canvas").getcontext("2d")); debugdraw.setdrawscale(10.0); debugdraw.setfillalpha(1); debugdraw.setlinethickness(1.0); debugdraw.setflags(b2debugdraw.e_shapebit | b2debugdraw.e_jointbit); world.setdebugdraw(debugdraw); // start drawing window.setinterval(update, 1000 / 100); } function update() { world.step(1 / 60, 8, 3); world.drawdebugdata(); world.clearforces(); }
wrapping up
accessing the device orientation api is actually pretty easy. using the information in a useful way is a whole other story. it really is too bad though that there isn't a performant javascript physics engine yet for mobile devices. on the other hand, with the speed the power of smartphones and tablets is increasing time might solve this.
Published at DZone with permission of Jos Dirksen, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments