Sunday, December 29, 2013

Device Orientation (Game Programming)

In my last post, I reviewed a charming classic game called Orbland. It looked like this:


And my review of Orbland is at http://firefoxosgaming.blogspot.com/2013/12/orbland-labyrinth-game-review.html  Orbland simulated a rolling ball in a maze. This isn't a game you can play on a PC! You have to tilt the phone to roll the ball and if you try to run this type of game on a PC browser, it won't work. It also won't work on Firefox for Android (yet). You need a phone to do program this kind of interaction, but how?

And speaking of Firefox for Android, if you have an Android, be sure to get Firefox for Android, because a lot of the game I have reviewed here will run on Firefox for Android, so who needs Google Play for games!

Not only is it fun to play games (on Firefox OS), but it is fun to make games. Today's post will introduce the bizarre world of Device Orientation, that is, twisting and turning your phone. And by the way, don't confuse this with Screen Orientation, which has to do with playing games in Portrait or Landscape. If you want to see that article, go here: http://firefoxosgaming.blogspot.com/2013/11/screen-orientation-and-moz-prefixes.html.

So how does Device Orientation work? It uses a specific event, deviceorientation, to determine changes in orientation. Are you turning the phone from top to bottom? side to side? rotating around like a top? If this sounds confusing, check out this page on Mozilla Developer Network: https://developer.mozilla.org/en-US/docs/Web/Guide/Event/Orientation_and_motion_data_explained, which explains the three ways you can rotate your phone, and this page, which explains a bit about the API: https://developer.mozilla.org/en-US/docs/WebAPI/Detecting_device_orientation.

Here's the code I use to do some simple ball-rolling. Load it into your phone and have a good time. Not as skillful as Orbland, but it does have a ball rolling around.

<!DOCTYPE HTML>
<html>
 
  <head>
    <meta charset="UTF-8">
    <title>
      Device Orientation
    </title>
 
    <style>
        
      #ball
      {
        width: 20px;
        height: 20px;
        position: absolute;
        top: 230px;
        left: 150px;
        background-image: url(ball.png);
      }
      
    </style>
   
       <div id="ball"></div>

    <script type="text/javascript">

        // Load event listener
      window.addEventListener("load", getLoaded, false);
     
      // deviceorientation event listener
      window.addEventListener("deviceorientation",
        handleOrientation, false);

        // Get ball information.
        var myBall = document.querySelector("#ball");
     
      // Function called on page load.
      function getLoaded() { 

            // Output to console.
        console.log("The page is loaded.");
      }
     
      function handleOrientation(evt) {
     
        // Beta is top/bottom rotation.
        myBeta = ~~evt.beta;
       
        // Gamma is left/right rotation.
        myGamma = ~~evt.gamma;
     
        // Test to see if tilt is in range.
        if ((myBeta < 29) && (myBeta > -29)
               && (myGamma < 29) && (myGamma > -29)) {
     
          // Calculate ball position for beta.
          myBString = 230 - (myBeta * 7) + "px";
         
          // Display ball position for beta.
          tilt.textContent = "beta " + myBString;
         
          // Move ball to new beta position.
          myBall.style.top = myBString;
       
          // Calculate ball position for gamma.
          myGString = 160 - (myGamma * 5) + "px";
         
          // Display ball position for gamma.
          tilt.textContent = tilt.textContent +
            " gamma " + myGString;
           
          // Move ball to new gamma position. 
          myBall.style.left = myGString;             
        }     
      }
     
    </script>
  </head>
 
  <body>
  <p id="tilt">Tilt!</p>
  </body>

</html>


Here's what the screen looks like if your phone is held flat (tangent to the earth's surface).


At the top I've printed the beta and gamma positions of the ball. Since the screen is 320 x 460 pixels, the beta will be the vertical position (216 pixels from the top edge, roughly half of the screen height) and the gamma will be the horizontal (160 pixels from the left edge, roughly half of the screen width). For more about width and height, see my post at http://firefoxosgaming.blogspot.com/2013/10/how-big-am-i-game-programming.html.

What is beta and gamma? As the MDN article https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Orientation_and_motion_data_explained explains, beta is the rotation from top to bottom, and gamma is the rotation from left to right. Or if you've ever ridden in a small boat, beta is rocking the boat from bow to stern, and gamma is rocking from port to starboard. There's a third Greek letter, alpha, that is like turning the boat around in circles (but because we don't want to get you seasick, alpha won't be covered here). Look at the excellent pictures on MDN for further explanation.

Essentially the code follows the same format as the CSS bouncing ball code example I wrote at http://firefoxosgaming.blogspot.com/2013/10/bouncing-ball-in-css-game-programming.html. You have a picture of a ball, you define it in CSS, and move it around. The movement is defined by changes in the deviceorientation event.

When the deviceorientation event is triggered, a check is made to see if the changes in device orientation are between -29 and +29. Device orientation runs from -180 to 180 (making a complete 360 degree circle) for beta and gamma, but it you don't want to know if the phone is tilting more than 30 degrees in any direction.

Here's my equation:

        // Test to see if tilt is in range.
        if ((myBeta < 29) && (myBeta > -29)
               && (myGamma < 29) && (myGamma > -29)) {


This uses the && to make sure that all four conditions are met. This evaluates to true only if all four conditions mean that the screen isn't tilting too much.

Also, before this equation, I did conversions that you may not have seen before:

        // Beta is top/bottom rotation.
        myBeta = ~~evt.beta;
       
        // Gamma is left/right rotation.
        myGamma = ~~evt.gamma;


These take the original value of the beta and gamma values and convert them from floating point to integer (whole number) values. There are JavaScript functions that will remove all the numbers to the right of the decimal point, but if you don't know whether the number is positive or negative, you can't use floor or ceil. But if you use a double ~~ (tilde), you can convert floaters to the correct values (-3.14 = -3, +3.14 = 3). Essentially ~ (tilde) is a bitwise NOT operator and it changes a number to a 32-bit integer and flips all the bits. And a second ~ (tilde) flips all the bits back, but now it's just an integer. This is cool because JavaScript doesn't have data typing for different kinds of numbers, but you can fool it with this obscure bit of magic. You can read more about it here: http://www.frontendjunkie.com/2012/06/deep-dive-into-javascript-not-bitwise.html

So here is how it works in practice:

Tilt the phone so the top is pointing down and read the beta and gamma values.


The beta is smaller but the gamma is still pretty much the same. Here's what happens if you tilt it so the bottom is tilted down.



And likewise, if you tilt it so the left side is down, you get:


And finally, if you tilt it toward the right side, you get:


Pretty cool, huh? The ball goes where you want, no tapping, no touching. A phone-only way of controlling your game. Simple in my sample, but cool in Orbland. If anyone sees other games that use this technique, let me know.

No comments:

Post a Comment