24 November 2015
Tutorial: Using the mobile/remote accelerometer
One of the cool things about the new Apple TV Remote is that it has a built-in accelerometer, and similar to mobile devices, this allows you to guage the device’s position/orientation in space. Essentially, on either a mobile device or the remote control, the accelerometer makes three discrete measurements based on the orientation and center point of the device.
Accelerometer events
The accelerometer event returns several useful values, including:
- event.name — This property will always be
"accelerometer"
. - event.isShake — This property will be
true
if a rapid movement of the device is detected. - event.xGravity / event.yGravity / event.zGravity — These events are probably the most useful. They are normalized, so you get a value between
-1.0
and1.0
. These values are also smoothed out to adjust for micro-movements and prevent jerky actions. - event.xInstant, event.yInstant, event.zInstant — These values report the change since the last event, for instance the current
event.xGravity
minus the lastevent.xGravity
. - event.xRaw, event.yRaw, event.zRaw — These values are the actual values from the device (there is no filtering or smoothing applied).
- event.deltaTime — Accelerometer events come at somewhat predictable intervals based on the value set by the system.setAccelerometerInterval() function, but they are not guaranteed to arrive on time. This is where the event.deltaTime comes in. It allows you to get the time between events so you can provide additional smoothing as needed.
All of the “gravity,” “instant,” and “raw” values assume that the device is being held in “portrait” orientation. This is very common for people holding the Apple TV Remote (the remote being held like a traditional TV/audio remote control). However, if you’re designing a landscape-oriented app like a racing game, you might want the remote to be held in “landscape” orientation (like a traditional console game controller) in order to emulate a steering wheel. In this case, you should adjust your values by 90 degrees.
Implementation
Let’s look at a simple implementation of the accelerometer. This app will create a blue circle and allow you to move it around the screen by moving/orienting the device.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
display.setDefault( "anchorX", 0 ) local dot = display.newCircle( display.contentCenterX, display.contentCenterY, 20 ) dot:setFillColor( 0, 0, 1 ) dot.color = "blue" dot.anchorX = 0.5 local xGravityLabel = display.newText( "xGravity:", 10, 15, native.systemFontBold, 12 ) local yGravityLabel = display.newText( "yGravity:", 10, 31, native.systemFontBold, 12 ) local zGravityLabel = display.newText( "zGravity:", 10, 47, native.systemFontBold, 12 ) local xGravity = display.newText( "", 80, 15, native.systemFont, 12 ) local yGravity = display.newText( "", 80, 31, native.systemFont, 12 ) local zGravity = display.newText( "", 80, 47, native.systemFont, 12 ) local xInstantLabel = display.newText( "xInstant:", 250, 15, native.systemFontBold, 12 ) local yInstantLabel = display.newText( "yInstant:", 250, 31, native.systemFontBold, 12 ) local zInstantLabel = display.newText( "zInstant:", 250, 47, native.systemFontBold, 12 ) local xInstant = display.newText( "", 330, 15, native.systemFont, 12 ) local yInstant = display.newText( "", 330, 31, native.systemFont, 12 ) local zInstant = display.newText( "", 330, 47, native.systemFont, 12 ) local function onTilt( event ) xGravity.text = event.xGravity yGravity.text = event.yGravity zGravity.text = event.zGravity xInstant.text = event.xInstant yInstant.text = event.yInstant zInstant.text = event.zInstant dot.x = dot.x + event.xGravity dot.y = dot.y + event.yGravity if dot.x > display.contentWidth then dot.x = display.contentWidth end if dot.x < 0 then dot.x = 0 end if dot.y > display.contentHeight then dot.y = display.contentHeight end if dot.y < 0 then dot.y = 0 end if event.isShake then if dot.color == "blue" then dot:setFillColor( 1, 0, 0 ) dot.color = "red" else dot:setFillColor( 0, 0, 1 ) dot.color = "blue" end end return true end Runtime:addEventListener( "accelerometer", onTilt ) |
For this simple example, we create a dot (circle), color it blue, and set up some text display objects to show the values received from the accelerometer event.
Accelerometer events require a listener function, served by our onTilt()
function. Inside the function, we update the on-screen values based on the current accelerometer data.
Next, we add the values of event.xGravity
and event.yGravity
to the dot’s x and y position. This will move the dot around the screen. Following this, we write four conditional statements to constrain the dot to the screen bounds.
In the final block of the function, we test to see if the device was shaken and swap the color between red and blue each time a shake is detected.
Finally, on the last line of the example, we enable the "accelerometer"
event handler.
Conclusion
Using the accelerometer is an important part of getting your apps to run properly on tvOS — after all, not every control scheme can be accomplished with simple up/down/left/right key events or axis inputs. For example, consider a classic game like “Fruit Ninja.” On a mobile device, you couldn’t use the accelerometer to slash the fruit, but now with an accelerometer-enabled remote, you could detect slashing motions by tracking the accelerometer’s values.
Andreas
Posted at 23:25h, 24 NovemberHi Rob,
Thanks, that’s just perfect! I’m almost done porting “Freeze! 2 – Brothers” from Portrait to Landscape and adding key controls, and the accelerometer implementation will be my next task. Your timing is awesome!
Best,
Andreas
Tobias
Posted at 05:09h, 26 NovemberVery useful informations – thanks a lot! Can’t wait porting our first game to Apple TV!
Usando acelerômetro com Corona SDK - LuizTools
Posted at 15:30h, 05 July[…] * Criado a partir do tutorial original Tutorial: Using the mobile/remote accelerometer […]