In the previous post, we got a working game that uses touch gestures to move the player’s ship. This may be adequate, but perhaps we can try a different approach: using the accelerometer.
Every Windows Phone device must have an accelerometer, which indicates its relative orientation in space. Tilting the phone from side to side may be more intuitive (and perhaps exciting) than simply dragging horizontally. Let’s try that.
The first thing we need to do is add a reference to the Microsoft.Devices.Sensors assembly, and add a conditional using statement to the Microsoft.Devices.Sensors namespace. This exposes the Accelerometer class that we can use to determine the orientation of the device.
Now that this is set, we can initialize the accelerometer and start getting some results:
_accel = new Accelerometer();
_accel.ReadingChanged += (sender, e) => {
_moveOffset = -Math.Min(15, 20 * (float)Math.Abs(e.Y)) * Math.Sign(e.Y);
};
_accel.Start();
The RadingChanged event is fired often, indicating a change in the device’s orientation. Here I update a field (_moveOffset) that is later used to change the position of the ship. Note that the event is raised on a different thread (not the UI thread), which may be an issue when working with a “true” UI, such as a Silverlight application. In that case, you may need to marshal the handler code to the UI thread. Here’s a simple example from a Silverlight app, that wants to display the readings from the accelerometer in three TextBlock objects:
_accel = new Accelerometer();
var sc = SynchronizationContext.Current;
_accel.ReadingChanged += (sender, e) => {
sc.Post(delegate {
_x.Text = e.X.ToString();
_y.Text = e.Y.ToString();
_z.Text = e.Z.ToString();
}, null);
};
_accel.Start();
This code uses the SynchronizationContext class to do the marshaling (an alternative would be to use the Dispatcher.BeginInvoke method). _x, _y and _z are TextBlock names.
In our game, this does not matter much, as we just update a variable. There is no “true” UI to be concerned about. There still may be a concern as _moveOffset may be updated while it’s being used by the UI thread within the game loop. This is not an issue here, as it’s a float, which means it should update atomically (the exact reason is out of scope for this post).
The X, Y and Z properties of the AccelerometerReadingEventArgs object are in the range –1.0 to +1.0, indicating the rotation angle around the relevant axis. In our case, the Y axis is the relevant one.
Now we can change the movement code, such as the following:
speed = Math.Abs(_moveOffset);
right = _moveOffset > 0;
left = _moveOffset < 0;
I’ve also commented out the horizontal drag gesture handling code, although we could have left that so the player can use either mechanism.
Testing
To test this, we could use the emulator, but really, in this case, there is no substitute for the actual device. Before testing, we have to indicate that we require access to the device’s sensors, otherwise a security exception will be thrown and our app terminated. This is done in the WMAppManifest.Xml:
<Capabilities>
<Capability Name="ID_CAP_NETWORKING" />
<Capability Name="ID_CAP_SENSORS" />
<!--<Capability Name="ID_CAP_LOCATION" />
<Capability Name="ID_CAP_MICROPHONE" />
<Capability Name="ID_CAP_MEDIALIB" />
<Capability Name="ID_CAP_GAMERSERVICES" />
<Capability Name="ID_CAP_PHONEDIALER" />
<Capability Name="ID_CAP_PUSH_NOTIFICATION" />
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
<Capability Name="ID_CAP_IDENTITY_USER" />
<Capability Name="ID_CAP_IDENTITY_DEVICE" />-->
<!-- Windows Phone OS 7.1 Capabilities -->
<!--<Capability Name="ID_CAP_ISV_CAMERA" />
<Capability Name="ID_CAP_CONTACTS" />
<Capability Name="ID_CAP_APPOINTMENTS" />-->
</Capabilities>
By default, a new project enables all capability flags. It’s a good idea to enable only the capabilities we actually require. That’s because in the marketplace, when an app is installed, it asks the user to agree to the app’s requested capabilities. The more capabilities requested, the greater chance the user will be “afraid” to let the app be installed. So, it’s better to request as few capabilities as possible. The networking capability, by the way, is not strictly required for our game, as we don’t do any networking, but it’s required so that the emulator can be contacted. it should be removed before uploading to the marketplace if not actually needed1.