Tilt Controls - Using device accelerometers as a game controller
Summary
Playing action games on a touch screen can be challenging. Some games go with a joystick-like on-screen controller. I had several issues with this approach. For starters, it takes up precious screen space on smaller devices like phones. Also, since it is a touchscreen, you have to train your brain to the precise locations of the different controls like up, down, left, and right. I find it very frustrating to have to play "game controller", rather than the game. By that I mean, you have to repeatedly look at the on-screen controller to press the right button.
There has to be a better way. Since mobile devices have accelerometers, why not use slight rotations of the device? For example, tilt the device to the left, and the player moves left. Tilt the device away, and the player moves up. It solves all the problems I mentioned of the on-screen controller, and allows for proportional control if you want. The more you tilt, the faster you move (up to a maximum limit.)
This post is about how GMGE's tilt controls work. Between some device-type quirkiness and when axis values roll over, it isn't nearly as straightforward as I wanted it to be.
Accelerometer Access
GMGE games, even when running full screen as a PWA, run within the constraints of the Web browser they are running in. On iOS devices, you have to ask for user permission, and have it granted, before being able to read acclerometer data. This is done with the following line of code.
Making that call on iOS devices spawns a system-modal dialog prompting the user to grant access. There is a caveat though. The call can only occur after a user interaction, like pressing a button. I typically have it trigger either from a Splash scene's continue button, or whatever button starts the first scene where you want tilt controls. If the player chooses no, it is game-over (pardon the pun).
You can't re-prompt the player in the same game session. iOS will automatically return "denied" on subsequent calls after a rejection. As such, you might want to inform your players that they need to grant access before requesting permission, so they know to press "Allow"
Axis Info
The three rotational axes are defined as follows:
- alpha - perpendicular to the screen; can be used as steering controls if you hold the device like a steering wheel. It ranges from 0 to 360 degrees
- beta - axis runs through the sides that are closer together. The left and right sides in portrait orientation. Rotation around the beta axis is forward and backward tilt, ranging from -180 to 180 degrees. beta is 0 when the device face up (alpha pointing straight up)
- gamma - In portrait orientation, it represents left to right tilt, ranging from -90 to 90 degrees. gamma is 0 when the device is perfectly face up or face down (alpha pointing straight up or straight down)
Orientation
Something to always keep in mind is that GMGE always runs in a browser of some form, even when running as a PWA (Progressive Web App), where it can run full-screen. There are implications to that. For starters is device orientation. Things running in a browser are not allowed to lock their orientation. That makes things more complicated than a native App that can specify and lock the device orientation while it is running. Since we can't do that, we have to support up to 4 orientations and changes between them. (The following descriptions are based on the player sitting in an upright position)
- portrait-primary: the typical way you hold your device upright when in portrait orientation. Camera at the top (gamma, as shown above, is pointing skyward)
- portrait-secondary: upside down portrait orientation, with the camera at the bottom. (Note, not all devices support this mode. While iPad does, iPhone does not. This can cause some quirkiness as we will soon see.) (gamma, as shown above, is pointing to the ground)
- landscape-primary: camera to the left (beta, as shown above, is pointing down)
- landscape-secondary: camera to the right (beta, as shown above, is pointing skyward)
GMGES allows you to specify a game orientation (if you want to), and while GMGE cannot prevent the player from rotating their device to a different orientation, it will pause the game and automatically display a message showing the proper orientation for playing. If you don't care about the orientation from your game's perspective, and you use tilt controls, then the game controller axes (x and y) need to respond correctly regardless of how the device is being held.
The operating system controls when the orientation changes, and there is nothing that you can do about it. In general this is not really an issue, but there are definitely cases where it can be. For example, if your player is leaning to one side, holding the device at an angle, tilting it might cross the orientation-change boundary and flip the display. Players will quickly learn those boundaries and likely avoid them. However, the game needs to play correctly regardless of the orientation.
Why do we care about orientation?
If your game uses tilt controls the device-orientation determines whether the beta or gamma axis for up and down (as well as left and right). GMGE needs to correctly map x and y controller axes based on orientation, as well as determine sign inversion. There are 2 main cases:
- Landscape vs Portrait: determines how to map beta and gamma to x and y
- Primary vs Secondary: determines if we need to flip the sign for x and/or y. For example, in landscape-primary tilting the top away increases gamma and is positive y. In contrast, in landscape-secondary, tilting the top away decreases gamma, but we want it to be positive y, so we have to flip its sign and report -y in that case.
Device tilt to player motion
When play starts, the player is likely holding their device in a neutral position. Of course that can be any arbitrary rotation of any and all axes. For example, a player could be seated upright, or laying on their back or side. That starting neutral position represents the tilt-base. Any device tilt away from the tilt-base represents intended motion.
GMGE assumes your game uses proportional motion by default, within a -10 to +10 degree range from the tilt-base. For example if you tilt the device up by 3 degrees, the y-axis value would be 0.3. Anything beyond 10 degrees in that case is capped at the y-axis being 1.0. That way if you tilt slightly perhaps the player walks, and more tilt causes the player to run. It is up to the game designer.
As mentioned there are 3 device axes that need to be translated into game-controller axes, and are impacted by device-orientation. There are some additional considerations in making it all work though.
Alpha, beta and gamma all represent 360 degress of rotation in different ways. As mentioned above alpha ranges from 0 to 360, where beta goes from -180 to 180. Gamma is even more different in that it ranges from -90 up to 90 where it flips to -90 and keeps counting up to 90 again. (Arguably gamma really only represents 180 degrees of motion, but it still works.)
Handling axis range boundaries
If a person is playing a game in landscape-primary orientation and holding the device in front of them while sitting upright, rotating the device forward and backward is based on the gamma axis. If the tilt-base has gamma set to say -83 degrees, then the range of motion, plus or minus 10 degress from the tilt-base, ranges from 87 up to -73 degrees!
Unfortunately, you read that correctly. Starting from -83 degrees, and moving down, as soon as the device goes beyond -90 degrees, it jumps to 90 degrees. This complicates the math, because each axis does this at a different point with different values. All the game cares about is how much to move on the x or y axis.
GMGE handles this by detecting if the absolute value of the difference between the current angle and the tilt-base is too large, then it crossed an axis boundary. It then adjusts the boundary limit within the engine to report the correct amount of tilt on the x or y axis. In the example above, if gamma is 88 degrees when the tilt-base is -83 degrees, GMGE would say the game-controller y-axis is -0.9, which is correct for landscape-primary. The following pseudo-code illustrates the process.
Something similar is done for alpha and beta. The game controller axis values are then set based on the current device orientation.
Currently rotation on the alpha axis is ignored. The reason for this is because the orientation-change-animations would make a driving type game rather annoying to play. If the range of motion was severely limited for your game, it could work, but that isn't very steering wheel-like. (For any kind of action game, the browser's animation of the orientation change is pretty disruptive.)
If at some point in the future orientation can be locked within PWAs, it will make sense to revisit this.









