Now we talk about APIs that are specific for Windows Phone devices. These APIs are part of the Microsoft.Devices.Sensors and System.Device.Location namespaces and require that you add a reference to your project. To use the APIs, perform the following steps for your game:
1. | Locate the Solution Explorer in Visual Studio.
|
2. | Right-click your project and select Add Reference (see Figure 1).
|
3. | Locate and select the System.Device assembly from the list of assemblies under the .Net tab, and press Add.
|
4. | Repeat Steps 1, 2, and 3 to add the Microsoft.Devices.Sensors assembly.
|
5. | Repeat Steps 1, 2, and 3 to add the Microsoft.Phone assembly.
|
6. | At the top of code files, include the following lines of code after the existing using statement:
using Microsoft.Devices.Sensors; using System.Device.Location;
|
Now you are ready to use all of
the great sensors and location services that are available on Windows
Phone devices. These include the accelerometer to read the acceleration
changes that occur to the phone, location services to enable you to
determine the phone’s position on the planet, and APIs to cause the
phone to vibrate.
Because the following APIs
are specific to Windows Phone devices, they are not available on all of
the other platforms supported by XNA Game Studio. If you build a
multiplatform game and want to share code, you need to write the Windows
Phone-specific code blocks in #if WINDOWPHONE blocks:
#if WINDOWS_PHONE
// Your Windows Phone specific code
#endif
Acceleration Data using the Accelerometer
All Windows Phone
devices provide access to an accelerometer sensor. An accelerometer is
used to measure force changes to the phone device from gravity or by
forces exerted by the user. The accelerometer sensor is sensitive enough
to determine when the user tilts the device slightly. It also works
great to detect when the user shakes or swings the phone.
Reading Acceleration Data
To read acceleration data, you need to construct a new instance of an Accelerometer. The Accelerometer
class is used to start, stop, and read back changes that occur to the
accelerometer while it is currently running. Create a member variable to
store the Accelerometer in your game class:
Accelerometer accelerometer;
This stores the instance
across your game class so you can access the accelerometer in other
parts of the game. To create an instance of the Accelerometer in your game’s Initialize method, add the following line of code:
accelerometer = new Accelerometer();
Unlike the XNA Game Studio input type, the Accelerometer uses an event-based model to return new data from the accelerometer. Data is returned by subscribing to the Accelerometer.ReadingChanged event and implementing an event handler method in your game. To subscribe to the ReadingChanged event, add the following lines of code after your previous line to create the instance of Accelerometer:
accelerometer.ReadingChanged += new
EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged);
Now, implement the accelerometer_ReadingChanged method that is called when the accelerometer receives new data. Add the following method to your game class:
public void accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
Vector3 accelerometerReading;
accelerometerReading.X = (float)e.X;
accelerometerReading.Y = (float)e.Y;
accelerometerReading.Z = (float)e.Z;
// Update velocity
spriteVelocity.X += accelerometerReading.X * spriteSpeed;
spriteVelocity.Y += accelerometerReading.Y * spriteSpeed;
}
The reading change event converts the AccelerometerReadingEventArgs parameter to something more useful for your game, which is a Vector3.
In the previous case, you just used a local variable. In your game, you
want to store this data or change another variable over successive
calls.
To have the accelerometer start reading data, you must call the Start method. In the example, we start the accelerometer right away in the game’s Initialize
method. For your game, you should not start to read accelerometer data
until it is needed. For example, you don’t need to start the
accelerometer when the game starts unless you use the accelerometer in
your menus. Wait until your level starts before starting the
accelerometer. Add the following lines of code after you set the ReadingChanged in your game’s Initialize method:
try
{
accelerometer.Start();
}
catch (AccelerometerFailedException e)
{
// Accelerometer not supported on this platform
// We are going to exit.
Exit();
}
The Start method throws a AccelerometerFailedException if the accelerometer is not supported on the platform or if you have already called the Start method. In the previous code, we exit the example game. In your game, you should determine what the best course of action is.
Now
that the accelerometer has started, your game should start to revive
reading changed events whenever the phone’s accelerometer detects
changes.
When your game is not using the accelerometer, you should call the Stop method to help save battery life and to help your game’s performance. For our example, we use the Game classes OnExiting method to put the call to the Stop method. The default game template does not create an override for OnExiting, so you need to add the overridden method to your game class. In your game class, add the following method:
protected override void OnExiting(object sender, EventArgs args)
{
try
{
accelerometer.Stop();
}
catch (AccelerometerFailedException e)
{
// Do nothing since we are already exiting
}
base.OnExiting(sender, args);
}
Similar to the Start method, the Stop method can also throw an exception. The AccelerometerFailedException is thrown when the accelerometer fails to stop or if it is already stopped.
Moving a Sprite Based on Accelerometer Data
Now that you have
the basics of getting accelerometer data, let’s do something more fun
and exciting. Like the previous examples, you move a sprite based on the
user input, but in this case use the accelerometer data.
To build on the previous code
example, you need some member variables to hold the sprite texture,
position, and velocity. Add the following member variables to your
game’s class:
Texture2D spriteTexture;
Vector2 spritePosition;
Vector2 spriteVelocity;
float spriteSpeed = 50.0f;
int screenWidth = 480;
int screenHeight = 800;
Next, load a texture to use as your sprite. In the game’s LoadContent method, add the following line of code:
spriteTexture = Content.Load<Texture2D>("XnaLogo");
Next, update the sprite’s velocity based on the returned acceleration data. To do this, update the accelerometer_ReadingChanged method you implemented previously. Add the following lines of code to the method:
// Update velocity
spriteVelocity.X += accelerometerReading.X * spriteSpeed;
spriteVelocity.Y += accelerometerReading.Y * spriteSpeed;
The sprite’s velocity is now updated based on the accelerometer reading changes and then scaled.
Now, create the sprite update logic that changes the sprite position based on the current velocity. In your game’s Update method, add the following lines of code:
// Update sprite position
spritePosition += spriteVelocity * (float)gameTime.ElapsedGameTime.TotalSeconds;
// Dampen velocity
spriteVelocity *= 1 - (float)gameTime.ElapsedGameTime.TotalSeconds;
// Check sprite bounds
if (spritePosition.X < 0)
{
spritePosition.X = 0;
spriteVelocity.X = 0;
}
else if (spritePosition.X > screenWidth - spriteTexture.Width)
{
spritePosition.X = screenWidth - spriteTexture.Width;
spriteVelocity.X = 0;
}
if (spritePosition.Y < 0)
{
spritePosition.Y = 0;
spriteVelocity.Y = 0;
}
else if (spritePosition.Y > screenHeight - spriteTexture.Height)
{
spritePosition.Y = screenHeight - spriteTexture.Width;
spriteVelocity.Y = 0;
}
After you update the
sprite’s position, you dampen the velocity so it slows over time. Then,
check the bounds of the sprite so it can’t travel off the screen.
Otherwise, it permanently flies off the screen. When the sprite hits one
of the walls, set the velocity for that direction to 0 to enable the
sprite to move easily off the wall.
The final step is to draw the sprite. To do this, add the following lines of code to your game’s Draw method:
// Draw the sprite
spriteBatch.Begin();
spriteBatch.Draw(spriteTexture, spritePosition, Color.White);
spriteBatch.End();
Autorotation and Acceleration Data
XNA
Game Studio games on Windows Phone support autorotation that enable you
as a developer to select which orientations work best for your game.
Autorotation can cause a couple of challenges when creating a game that
uses the accelerometer.
The first issue is that if your
game supports multiple orientations and you expect the user to move and
tilt the phone, it could cause the orientation of the game to switch
when the user does not intend to change orientations. It is problematic
if the screen rotates in the middle of playing a flight simulator
evading incoming missiles. To address this concern, you can limit your
supported orientations and test that the accelerometer actions you
expect your users to use don’t trigger the orientations during normal
game play. If you find that the orientations are triggered, you can set
the supported orientations to just a single orientation and enable users
to change the orientation using a menu option in your game.
The other issue is that when
your game changes orientations, the acceleration data does not change.
If your game supports multiple orientations, your game needs to take
into account the current orientation and how that maps to the
accelerometers X,Y, and Z coordinates.