Logo
HOW TO
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
 
 
Windows Phone

Handling Input on Windows Phone 7 : Accelerometer

10/21/2012 5:46:43 PM
The accelerometer can be an entertaining and engaging method of input, especially for game development with XNA Game Studio or Silverlight. We all have seen the car racing games on mobile phone or mobile gaming devices where the user is tilting the device like a steering wheel.
1. Understanding How It Works

The Accelerometer sensor detects acceleration in all three axis's, X, Y, Z to form a 3D vector. You may wonder in what direction and magnitude does the vector point?Collect a few accelerometer readings using this line of code

System.Diagnostics.Debug.WriteLine(AccelerometerHelper
.Current2DAcceleration.ToString());

The following are a few samples from the Output window when debugging:

{X:0.351 Y:-0.002 Z:0.949} (Magnatude is approximately 1.02)
{X:0.401 Y:0.044 Z:0.984} (Magnatude is approximately 1.06)
{X:0.378 Y:0.04 Z:1.023} (Magnatude is approximately 1.09)
{X:0.386 Y:0.022 Z:0.992} (Magnatude is approximately 1.06)
{X:0.409 Y:0.03 Z:0.992} (Magnatude is approximately 1.07)

You can calculate the magnitude of the vector using the Pythagorean Theorem, which is the Sqrt(X2+Y2+Z2) = magnitude of the vector. The value should be about one but as you can see from the above samples, it can vary by location or could possibly be an error deviation. Either way, this is why applications like a level suggest that you calibrate the level against a known flat surface before using the virtual level.

If you run the application in the emulator, this reading is returned every time: {X:0 Y:0 Z:-1}. Holding the phone flat in my unsteady hand yields similar values with Z near one and X, and Y near zero.

{X:0.039 Y:0.072 Z:-1.019}
{X:0.069 Y:0.099 Z:-1.047}
{X:0.012 Y:0.056 Z:-1.008}
{X:0.016 Y:0.068 Z:-1.019}

This suggests that the vector is oriented to point towards the center of the earth, which for the above readings is the bottom of the phone or a negative Z when the phone is lying flat on its back. Flipping the phone in my hand yields the following values:

{X:-0.043 Y:0.08 Z:1.019}
{X:-0.069 Y:0.111 Z:1.093}
{X:-0.069 Y:0.099 Z:1.093}
{X:-0.039 Y:0.107 Z:1.031}

This time the vector is pointing out from the glass toward the ground, because the phone is lying face down. This information is useful if you need to determine how the phone is oriented in the users hand when say taking a photograph.

Figure 1 shows the accelerometer coordinate system. This is important because developers must translate readings into the coordinate system for the application.

Figure 1. Accelorometer fixed coordinate system

As an example, in the XNA Framework, the default 2D coordinate system has positive Y goingdown, not up, so you cannot just take the Y component of acceleration and apply it to the Y value for a game object in 2D XNA.

NOTE

The default coordinate system for 3D in the XNA Framework has positive Y going up. 

With this background in hand, the next section covers development with the accelerometer sensor.

2. Programming with the Accelerometer

Accessing the Accelerometer sensor is pretty straightforward. We start with an XNA project, adding a reference to the Microsoft.Devices.Sensors assembly, and declaring instance of the Accelerometer class. In the Game1.Initialize() method, create an instance of the Accelerometer and call the Start() method to generate readings.

NOTE

Turn off the Accelerometer if not needed to save battery power.

Create an event handler for ReadingChanged as well. The following is the code to create the event handler:

accelerometer = new Accelerometer();
accelerometer.Start();
accelerometer.ReadingChanged +=
  new EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged);

					  

The accelerometer_ReadingChanged event handler event arguments AccelerometerReadingEventArgs class exposes acceleration in three dimensions via the X, Y, and Z member variables of type double. There is also a TimeStamp variable to allow measurement of acceleration changes over time.

A private member Vector3 variable named AccelerometerTemp is added to the Game1 class to collect the reading so that the code in the event handler does not have to new up a Vector3 each time a reading is collected. We create a helper static class named AccelerometerHelper that takes the accelerometer reading and assigns it to a Vector3 property named Current3DAcceleration. The following is the ReadingChanged event handler:

private Vector3 AccelerometerTemp = new Vector3();
void accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
  //
  AccelerometerTemp.X = (float)e.X;
  AccelerometerTemp.Y = (float)e.Y;
  AccelerometerTemp.Z = (float)e.Z;

  AccelerometerHelper.Current3DAcceleration  = AccelerometerTemp;
  AccelerometerHelper.CurrentTimeStamp = e.Timestamp;
}

					  

The AccelerometerHelper class takes the Vector3 and parses the values up in the "setter" function for the Current3DAcceleration property into the class members listed in Figure 2.

Figure 2. AccelerometerHelper class members

Most of the code in the AccelerometerHelper class is properties and private member variables to hold values.Private backing variables are optional with the {get ; set ;} construct in C# but we use them here in order for all other member variables to be derived from just setting the Current3DAcceleration property. Listing 1 has the code for the AccelerometerHelper class.

Example 1. AccelerometerHelper Class Code File
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace AccelerometerInputXNA
{
  static public class  AccelerometerHelper
  {
    static private Vector3 _current3DAcceleration;
    static public Vector3 Current3DAcceleration
    {
      get
      {
        return _current3DAcceleration;
      }
      set
      {
        //Set previous to "old" current 3D acceleration
        _previous3DAcceleration = _current3DAcceleration;

        //Update current 3D acceleration
        //Take into account screen orientation
        //when assigning values
        switch (Orientation)
        {
          case DisplayOrientation.LandscapeLeft:
            _current3DAcceleration.X = -value.Y;

					  

_current3DAcceleration.Y = -value.X;
            _current3DAcceleration.Z = -value.Z;
            break;
          case DisplayOrientation.LandscapeRight:
            _current3DAcceleration.X = value.Y;
            _current3DAcceleration.Y = value.X;
            _current3DAcceleration.Z = value.Z;
            break;
          case DisplayOrientation.Portrait:
            _current3DAcceleration.X = value.X;
            _current3DAcceleration.Y = value.Y;
            _current3DAcceleration.Z = value.Z;
            break;
        }

        //Update current 2D acceleration
        _current2DAcceleration.X = _current3DAcceleration.X;
        _current2DAcceleration.Y = _current3DAcceleration.Y;
         //Update previous 2D acceleration
        _previous2DAcceleration.X = _previous3DAcceleration.X;
        _previous2DAcceleration.Y = _previous3DAcceleration.Y;
        //Update deltas
        _xDelta = _current3DAcceleration.X - _previous3DAcceleration.X;
        _yDelta = _current3DAcceleration.Y - _previous3DAcceleration.Y;
        _zDelta = _current3DAcceleration.Z - _previous3DAcceleration.Z;
      }
    }

    static private Vector2 _current2DAcceleration;
    static public Vector2 Current2DAcceleration
    {
      get
      {
        return _current2DAcceleration;
      }
    }

    static private DateTimeOffset _currentTimeStamp;
    static public DateTimeOffset CurrentTimeStamp
        {
      get
      {
        return _currentTimeStamp;
      }
      set
      {
        _previousTimeStamp = _currentTimeStamp;
        _currentTimeStamp = value;
      }
    }

    static private Vector3 _previous3DAcceleration;
    static public Vector3 Previous3DAcceleration
    { get { return _previous3DAcceleration; } }

					  

static private Vector2 _previous2DAcceleration;
    static public Vector2 Previous2DAcceleration
    { get { return _previous2DAcceleration; } }

    static private DateTimeOffset _previousTimeStamp;
    static public DateTimeOffset PreviousTimeStamp
    { get { return _previousTimeStamp; } }

    static private double _xDelta ;
    static public double XDelta { get { return _xDelta;} }

    static private double _yDelta;
    static public double YDelta { get { return _yDelta; } }

    static private double _zDelta;
    static public double ZDelta { get { return _zDelta; } }

    public static DisplayOrientation Orientation { get; set; }
  }
}

Notice in the setter function for the Current3DAcceleration property, there is a switch statement that flips the sign as needed based on device orientation, whether landscape left or landscape right, because the accelerometer coordinate system is fixed. This ensures that behavior is consistent when the XNA Framework flips the screen based on how the user is holding the device

To test the helper class, we copy over the GameObject class and assets, StickMan and the font, from the GesturesTouchPanelXNA project as well as the code to load up the assets and draw on the screen. The gesture code is not copied over since the input for this project is the Accelerometer. As before, the Game1.Update() method in the game loop is the place to handle input and apply it to objects. This line of code is added to the Update method to apply acceleration to the StickMan GameObject instance:

StickManGameObject.Velocity += AccelerometerHelper.Current2DAcceleration;

Run the application and the application behaves as expected: tilt the phone left, and the StickMan slides left, and vice versa when holding the phone in landscape orientation. If you tilt the phone up far enough, the screen flips to either DisplayOrientation.LandscapeLeft or DisplayOrientation.LandscapeRight and the behavior remains consistent.

The main issue with this calculation of just adding the Current2DAcceleration to the StickMan's velocity results in a very slow acceleration. This can be easily remedied by scaling the value like this

StickManGameObject.Velocity +=30* AccelerometerHelper.Current2DAcceleration;

					  

The UI "feels" much better with this value and is more fun to interact with. Depending on the game object's desired behavior, you could create a ratchet effect by having fixed positions when the accelerometer values are between discrete values instead of the smooth application of accelerometer values to position in this code sample.

The Accelerometer sensor works equally well in Silverlight. The difference is mapping Accelerometer changes to X/Y position values directly (instead of applying Vectors) using a CompositeTransform object just like what was done in the Silverlight sample with Manipulations in the previous section on multi-touch. Next up is the Location sensor.

Other -----------------
- XNA Game Studio 4.0 : XNA Game Studio Storage (part 2) - Getting a Device
- XNA Game Studio 4.0 : XNA Game Studio Storage (part 1) - Recreating the Project on Xbox, Devices and Containers
- XNA Game Studio 4.0 : Storage - Isolated Storage
- Using Media in XNA Game Studio : Visualizations
- Using Media in XNA Game Studio : Video
- XNA Game Studio 4.0 : Dynamic Sound Effects - Recording Audio with a Microphone, Generating Dynamic Sound Effects
- XNA Game Studio 4.0 : Playing Sound Effects (part 2) - Microsoft Cross-Platform Audio Creations Tool
- XNA Game Studio 4.0 : Playing Sound Effects (part 1) - Using SoundEffect for Audio Playback
- Windows Phone 7 : Using MVVM and Performing Unit Testing
- Windows Phone 7 : Implementing MVVM on Windows Phone by Using MVVMLight
 
 
REVIEW
- First look: Apple Watch

- 10 Amazing Tools You Should Be Using with Dropbox
 
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
 
Popular tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS
Popular keywords
HOW TO Swimlane in Visio Visio sort key Pen and Touch Creating groups in Windows Server Raid in Windows Server Exchange 2010 maintenance Exchange server mail enabled groups Debugging Tools Collaborating
Top 10
- Microsoft Excel : How to Use the VLookUp Function
- Fix and Tweak Graphics and Video (part 3) : How to Fix : My Screen Is Sluggish - Adjust Hardware Acceleration
- Fix and Tweak Graphics and Video (part 2) : How to Fix : Text on My Screen Is Too Small
- Fix and Tweak Graphics and Video (part 1) : How to Fix : Adjust the Resolution
- Windows Phone 8 Apps : Camera (part 4) - Adjusting Video Settings, Using the Video Light
- Windows Phone 8 Apps : Camera (part 3) - Using the Front Camera, Activating Video Mode
- Windows Phone 8 Apps : Camera (part 2) - Controlling the Camera’s Flash, Changing the Camera’s Behavior with Lenses
- Windows Phone 8 Apps : Camera (part 1) - Adjusting Photo Settings
- MDT's Client Wizard : Package Properties
- MDT's Client Wizard : Driver Properties
 
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
2015 Camaro