Site Chooser

Ernie Elf’s Christmas Message – now on Mobile!

Hello ho ho!

Well, after another couple of days of festive fun and (far to much) food I sat down and decided I’d update Ernie Elf’s Christmas Message so that it worked on iOS (and Android) devices.

Previously, it only supported movement using the Left and Right keys on a keyboard, meaning iOS (and Android) devices were rather out of luck. In fact, it was worse than that: the initial introduction screen required a Space key press to close and start the game.

Today I decided to fix that:

  • To start the game, you tap the screen.
  • To move Ernie Elf, you tilt your device to the left and right.

Simple! And reasonably easy to play, as you can see here:

But don’t just take my word for it: pick up your iOS (or Android (Ice Cream Sandwich or above!)) device and play here:

play-christmas-game-no-keyboard

What I Did

I worked on this witha mixture of Internet research and trial and error to understand how the accelerometer worked. I developed a simple Accelerometer Test, which uses the combination of the x and y accelerometers plus orientation to determine how far left/right the screen is tilted in relation to the current orientation.

I then integrated this code into the game, using the output to determine Ernie’s horizontal (based on current orientation) velocity.

How To Do It

(A.k.a. The way I did it… this is in no way an endorsement, but might give you some ideas. 🙂 )

Define Global Variables
The first thing to do is to create some global variables that store a) our final value used to move our hero and b) interim orientation values used to calculate the final value:

/* Our end result variable - the Left/Right accelerometer value used to move our hero!*/
var accel = 0;
// Flag to determine if our device is "upside down"
var upsideDown = false;
// Flag to determine if our device is in landscape or portrait mode
var landscapeOrientation = false;

Orientation: Define Event Listener
Then we create a JavaScript event listener to react to changes in the device’s orientation (i.e. react when the device is turned from portrait to landscape or vice versa). We’ll be using this when determining our “horizontal” speed later:

window.onorientationchange = function() { orientationChanged(); };

Orientation: Create Event Handler Function
Now we create the orientation changing handler function “orientationChanged”. This checks the angle of orientation passed to the browser by the device, which is always -90, 0, 90 or 180:

function orientationChanged() {
/* Let's look at the device orientation passed to the browser and set variables based on its value:*/
switch(window.orientation) {
case 90:
// It's landscape mode and "upside down".
upsideDown = true;
landscapeOrientation = true;
break;
case -90:
// It's landscape mode and the "right way up".
upsideDown = false;
landscapeOrientation = true;
break;
case 180:
// It's portrait mode and "upside down".
upsideDown = true;
landscapeOrientation = false;
break;
default:
// It's portrait mode and the "right way up".
upsideDown = false;
landscapeOrientation = false;
break;
}
}

(The actual code I’ve used is more efficient, but the code shown here’s a bit easier to read).

Accelerometer: Define Event Listener
Ok, so we’ve got our orientation sorted – now we need to take a look at the accelerometer to see in fine detail the amount device is rotated by on its various axes. First we create an event listener to respond to the accelerometer and call an appropriate handler function:

window.addEventListener( 'devicemotion', onDeviceMotion, false );

Accelerometer: Create Event Handler Function
Finally we create the accelerometer updating handler function “orientationChanged”. This’ll allow us to determine our final “horizontal speed”, stored in the “accel” variable:

function onDeviceMotion() {
// Grab the accelerometer values
var accelX = event.accelerationIncludingGravity.x;
var accelY = event.accelerationIncludingGravity.y;
var accelZ = event.accelerationIncludingGravity.z;
/* If we're in landscape mode, set our final accel value to that of the Y accelerometer - otherwise, if in portrait, set accel to that of the X accelerometer. */
accel = (landscapeOrientation) ? accelY : accelX;
/* If we're upside down, reverse the horizontal "direction" of our final accel value. If we don't do this, our character will move in the opposite direction to that desired when our device is upside down. */
if (upsideDown) accel *= -1;
}

The result of this function is that our final variable, “accel”, will reflect how much we’ve tilted our device left and right based on the current orientation. We use this variable in moving our character, so that if we tilt left, accel’s value is negative so our character moves left, and vice-versa for right/positive.

Event Listener Declaration: Ordering
As a final note, when declaring the orientation and accelerometer event handlers, we should manually call the orientation event handling function before setting the accelerometer event handler. This means we’ll’ve correctly set the orientation variables (used by the accelerometer event handler function) without the user needing to rotate their device first:

window.onorientationchange = function() { orientationChanged(); };
orientationChanged(); // Manually call it!
window.addEventListener( 'devicemotion', onDeviceMotion, false );

Android Angst

You’ll notice I’ve been putting Android in brackets when mentioned. That’s because I’m not 100% sure it’s going to work as intended.

For a start, this only works on Android 4.0 (Ice Cream Sandwich) and above, as that’s when Google adopted the native browser accelerometer functionality introduced previously in iOS 4.0.

Also, on a Samsung Galaxy S2 this was tested on the angles reported by the orientationchange event appear to be 180 degrees out from iOS. This means that the “upsideDown” flag is set incorrectly at all times and so Ernie moves in the opposite direction to that which he should! Eek.

To fix this I’ve added a simple Android OS check and said “If it’s Android, then inverse Ernie’s direction”. It’s dirty, but it works. At least on an S2.

I’ve not got an ICS+ Android device to directly test this on (I’ve been remotely testing by hassling my mate Reef (thanks Reef!!) and didn’t want to drive her insane 🙂 ), and it looks like it might be different on different devices from what I read on Stackoverflow. Fun!

If you’ve any idea why iOS and Android (well, ICS on an S2) are reporting their orientation angles with what seems like a 180 degree difference, please let me know!

Anyway… it’s alive on iOS and Android! Wooh!

UPDATE (28/12/2012 13:57): I’ve added touch-based movement functionality for Android-only OSes of less than 4.0 – so you can now play this on Android < 4.0. When you play, you'll see two big translucent buttons on the left and right of the screen. Touch them to move Ernie! Pat P.S. Big thanks to Gary for his extensive testing help on iPad and iPhone and Reef for her remote testing on the S2!

Posted on Fri 28 Dec in Coding,Creative,Game with No Comments

Place your comment

Name

Email

Website

Please fill your data and comment below:

Browse

Follow

My blog runs on Wordpress, which uses cookies. By using my blog, you are consenting to this. Find out more

The cookie settings on this blog are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Further details on Wordpress's use of cookies is shown here.

Close