Detecting motion with accelerometer hardware

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

Use your Windows 8.1 or Windows Phone 8.1 device as the controller in your apps and games.

Move it!

If you have written any kind of interactive game or augmented reality app for iOS, you've probably used the UIAccelerometer class to get information on the device's current orientation or movement. With the ever-growing number of devices in tablet format, apps which use movement are going to become more and more popular. In this topic, we'll look at the different techniques at your disposal for detecting movement and orientation, and how you might use them in an app or game. Remember that desktop computers and many laptops will not have the necessary hardware sensors to provide this information, so don't make your app depend on their existence without clearly informing your potential users.

Note  When developing code which tests orientation or position, you will want to disable the system's ability to auto-rotate the screen. To do this, open the app manifest file (Package.appxmanifest) and select only the supported rotations you require. This will prevent the app from rotating the screen and confusing the user (and your code).

 

Available sensors

Both Windows 8.1 and Windows Phone 8.1 provide the ability to read multiple sensors. Not all sensors will be available on a specific platform, so it's important to test for them before expecting them to work. Here's a list of sensors, and when you might use them.

Sensor class Scenario
Accelerometer Detecting device movement, for example shaking or vibration.
Inclinometer Proving information on current yaw, pitch and roll of the device. Similar in function to UIAccelerometer in iOS.
Gyrometer Detecting rotational movement, for example, simulating a steering wheel.
Compass Returning current heading with respect to North (true, and magnetic). Useful for navigation and augmented reality apps.
SimpleOrientation Returning the current orientation as one of four states (portrait mode up/down, landscape mode left/right).

 

Writing a motion game

Here's the start of a C# game in which the player tilts their device in order to move a virtual ball across the screen. You'll have to add logic for detecting virtual obstacles, walls, holes and so on. This code assumes you've started with a blank store project using C#.

1. The XAML code

We'll start by defining the XAML code that makes up our page. We'll need to be able to move an <Image> object around the screen, so we'll use a XAML <Canvas> inside the top-level <Grid>. Using a canvas object means we can set the X and Y co-ordinates of the image. We'll also need to add a handler which is called when the canvas has finished loading: this handler will allow us to get the dimensions of the canvas control, as these will vary depending on the size of the display running the app.

Replace the top-level <Grid> definition with this code:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Canvas x:Name="canvas" Loaded="onLoad">
            <Image x:Name="ball" Source="Assets/ball.png" Height="80"  Width="80"/>
        </Canvas>
    </Grid>

This code also assumes you have a PNG image called ball.png already imported into your project. If you aren't going to be using PNG files, you could use a XAML <Ellipse>, like this:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Canvas x:Name="canvas" Loaded="onLoad">
            <Ellipse x:Name="ball" Height="80"  Width="80" Fill="White"/>
        </Canvas>
    </Grid>

2. The C# code

First, add the following using statements. The System.Diagnostics is to provide us with Debug.WriteLine, the Windows equivalent of NSLog.

using Windows.UI.Core;
using Windows.Devices.Sensors;
using System.Diagnostics;

Here's the rest of the class. Change the namespace to match your project title.

namespace myMovingGame
{
    public sealed partial class MainPage : Page
    {
        private Inclinometer inclinometer;
        private double ballX, ballY;
        private double dx = 0, dy = 0;
        private double screenWidth, screenHeight;
        private double ballSize = 40;

        public MainPage()
        {
            this.InitializeComponent();

            // Start the game loop to update the ball every frame
            CompositionTarget.Rendering += gameLoop;

            // Create the inclinometer, and start reading updates
            inclinometer = Inclinometer.GetDefault();

            if (inclinometer != null)
            {
                // Establish the report interval for all scenarios
                uint minReportInterval = inclinometer.MinimumReportInterval;
                uint reportInterval = minReportInterval > 16 ? minReportInterval : 16;
                inclinometer.ReportInterval = reportInterval;

                // Establish the event handler
                inclinometer.ReadingChanged += new TypedEventHandler<Inclinometer, InclinometerReadingChangedEventArgs>(ReadingChanged);
            }
            else
            {
                // Not all devices have suitable hardware
                Debug.WriteLine("No inclinometer present, sorry!");
                canvas.Visibility = Visibility.Collapsed;
            }
        }

        // Called when the hardware detects orientation change
        private async void ReadingChanged(object sender, InclinometerReadingChangedEventArgs e)
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
           {
               InclinometerReading reading = e.Reading;
               dx = reading.RollDegrees / 5;
               dy = reading.PitchDegrees / 5;
           });
        }

        // Gameloop called every frame update
        public void gameLoop(object sender, object e)
        {
            // Update the ball's co-ordinates and reposition it on scree
            ballX += dx;
            ballY += dy;

            if (ballX < ballSize) ballX = ballSize;
            if (ballX > screenWidth - ballSize) ballX = screenWidth - ballSize;
            if (ballY < ballSize) ballY = ballSize;
            if (ballY > screenHeight - ballSize) ballY = screenHeight - ballSize;

            ball.SetValue(Canvas.LeftProperty, ballX);
            ball.SetValue(Canvas.TopProperty, ballY);
        }

     private void onLoad(object sender, RoutedEventArgs e)
        {
        
         // Here is the earliest we can get the actual size of the canvas
         // object.

         // Must use ActualHeight/ActualWidth
            screenHeight = canvas.ActualHeight;
            screenWidth = canvas.ActualWidth;

            // Initialize ball to be in the center of the screen
            ballX = screenWidth / 2;
            ballY = screenHeight / 2;
        }
    }

There are a few important things to note:

  • We're using an inclinometer object rather than an accelerometer object. The inclinometer gives a snapshot of the current orientation of the device, not how it's changing, and this info is more suited to this particular app. The code is mostly lifted from the topic Quickstart: Determining pitch, roll, and yaw with the inclinometer (C#).

  • We set up a gameLoop method, and attach it to CompositionTarget.Rendering which means it gets called every frame. The gameLoop updates the current position of the player's ball, and checks that it hasn't gone off the screen.

  • The inclinometer object includes a callback which fires when there is a change in the device's orientation. When we get the callback, we take the new yaw and pitch values, shrink them a little, and then use them to update the balls' position. As the callback is only called when there is a change in the device's orientation, we do the actual updating of the position in the gameLoop.

  • The current size of the XAML canvas object is only available once the app's window is displayed. This is why we use the canvas onLoad method to get the information and use it set the starting position of the ball.

This is only a very simple starting point, but even so, the virtual ball still moves in quite a realistic way around the screen. When running on a Windows 8 tablet, it looks like this:

Using similar code, you can experiment with new ways of interacting with your Windows apps and games.

Responding to motion and orientation sensors (Windows Store apps using C#/VB/C++ and XAML)

Integrating devices, printers, and sensors (JavaScript)

Motion and device orientation for complex apps (Windows Store apps)