Table of Contents
Last month (September 2011) I received very great news: the Windows PHone SDK 7.1 had just been released. I opened the "What's New" session and it was
full of nice new features. I was particularly interested in the new Windows
Phone Emulator, which now has the ability to emulate device sensors, such as the Windows Phone Accelerometer.
This article is intended to show how to play with this new accelerometer emulation, accompanied by a simple application that use it.
To use the Windows Phone Labyrinth provided with this article, you must download and install the following 100% free development tool directly from Microsoft:
Visual Web Developer 2010 Express for Windows Phone
Whether you’re familiar with, or new to, Silverlight and XNA Game Studio programming, Visual Studio 2010 Express for Windows Phone provides everything you need to get started building Windows Phone apps.
Windows Phone SDK 7.1
The Windows Phone Software Development Kit (SDK) 7.1 provides you with all of the tools that you need to develop applications and games for both Windows Phone 7.0 and Windows Phone 7.5 devices.
The new Windows Phone SDK 7.1 and the new documentation are now packed up with lots of new features:
- Multi-targeting and App Compatibility
- Multitasking
- The Execution Model and Fast Application Switching
- Alarms and Reminders
- Background Agents
- Background File Transfers
- Background Audio
- Media
- Silverlight 4
- Sensors
- Sockets Support
- Network Information
- Push Notifications
- Live Tiles
- Silverlight and XNA Integration
- Application Performance Analysis
- Visual Basic Support
- Advertising
- WebBrowser Control
- Device Status
- Local Database
- Isolated Storage Explorer
- Launchers and Choosers
- Contacts and Calendar
- Encrypted Credential Store
- User Experience Design Guidelines
- Certification Requirements
- Marketplace Test Kit
- Camera
- Pictures Extensibility
- Search Extensibility
- Application Bar
- On-Screen Keyboard
- System Tray and Progress Indicator
- OData Client
- Globalization and Localization
- Creating Trial Applications
From all these features, the one we will be dealing with in this article is Sensors. But not all kinds of sensors (since "sensors" include accelerometer,
gyroscope sensors, combined motion and rotation rate). For now, we will play only with the accelerometer. Hopefully, in a near future we can focus on other features of WP7.
To access the new accelerometer emulator is quite easy. First, launch Windows Phone emulator. Then move the mouse over the emulator window. At this point, the same vertical icon bar
you found in the previous version of the emulator will be shown. The difference is the ">>" icon that you can spot at the bottom of the icon bar. Click it and a new "Additional Tools"
window will be opened:
Only that first tab, "Accelerometer", matters to us. It has the 3D rendering of the device (notice that this rendering doesn't show the same screen of the emulator). It's a fake
screen with static tiles. But hey, Microsoft, it would be very cool to have here the actual device screen, don't you think?
The second thing to notice in the accelerometer emulator is the Orientation dropdown list. We need first to pick the correct orientation (portrait or landscape and standing or flat)
to correctly perform the emulation. Our application will only work correctly if the device is in the horizontal position, so you must choose either Portrait Flat or Landscape Flat orientation.
The second thing to notice in the accelerometer emulator is the little red circle at the center of the window. Drag it and move it around the screen in order to emulate the accelerometer.
As you move the accelerometer control point around, the X, Y and Z coordinates change and are displayed on the screen. Also, this data is also passed to the Windows Phone Emulator
in real time, so any changes you make can be read by the application.
In code, the accelerometer functions are part the Microsoft.Devices.Sensors namespace:
using Microsoft.Devices.Sensors;
As soon as the application begins, we start the accelerometer, which fires the update event every 20 millisecond:
private void StartAccelerometer()
{
if (accelerometer == null)
{
accelerometer = new Accelerometer();
accelerometer.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
accelerometer.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(accelerometer_CurrentValueChanged);
}
accelerometer.Start();
}
When the accelerometer sensor value is changed, we simply change the gravity parameter of the World object. The World object
is used to manage all other objects in Farseer:
void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
{
if (World != null)
{
World.Gravity = new Vector2(e.SensorReading.Acceleration.Y * -ACC_GRAVITY_COEF, e.SensorReading.Acceleration.X * -ACC_GRAVITY_COEF);
}
}
Farseer is a wonderful open source physics engine, based on the original Box 2D open source project
(as an aside, the Angry Birds game uses Box2D). The difference is that Box2D is written in C++ (and has been ported to many languages), while Farseer is made with C#
aiming Silverlight and XNA. Also, the Farseer website claims this framework has many other features other than the ones provided by the original Box2D release.
By default, Farseer renders a circle object as an ellipse with a certain border and texture. Our ball is nothing more than a Farseer circle rendered with a static texture
of a steel ball. We first load the ball texture and define the initial position of the ball:
public override void LoadContent()
{
base.LoadContent();
LoadTextures();
DefineScreenRectangle();
InitializeBall();
InitializeLabyrinth();
StartAccelerometer();
}
private void InitializeLabyrinth()
{
List<Vertices> verticesList = GetVerticesFromLabyrinthTexture();
ScaleVertices(verticesList);
CreateLabyrinthBody(verticesList);
}
private void CreateLabyrinthBody(List<Vertices> verticesList)
{
labyrinth = BodyFactory.CreateCompoundPolygon(World, verticesList, 1f, BodyType.Dynamic);
labyrinth.BodyType = BodyType.Dynamic;
labyrinth.IsStatic = true;
}
private void ScaleVertices(List<Vertices> verticesList)
{
Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(1)) * SCALE_FOR_WP_RESOLUTION;
foreach (Vertices vertices in verticesList)
{
vertices.Scale(ref vertScale);
}
}
private List<Vertices> GetVerticesFromLabyrinthTexture()
{
Vertices textureVertices;
uint[] data = new uint[labyrinthTexture.Width * labyrinthTexture.Height];
labyrinthTexture.GetData(data);
textureVertices = PolygonTools.CreatePolygon(data, labyrinthTexture.Width, false);
Vector2 centroid = -textureVertices.GetCentroid();
textureVertices.Translate(ref centroid);
labyrinthOrigin = -centroid;
textureVertices = SimplifyTools.ReduceByDistance(textureVertices, 4f);
return BayazitDecomposer.ConvexPartition(textureVertices);
}
private void InitializeBall()
{
Vector2 startPosition = new Vector2(-20f, -11f);
Vector2 endPosition = new Vector2(20, -11f);
ball = new Ball(World, this, startPosition, 1.5f, false);
ball.BallCollided += new Labyrinth.Ball.BallCollisionEventHandler(ball_BallCollided);
}
private void DefineScreenRectangle()
{
var rectangle = ScreenManager.GraphicsDevice.Viewport.Bounds;
screenRectangle = new Rectangle(-rectangle.Width, -rectangle.Height, rectangle.Width * 2, rectangle.Height * 2);
}
private void LoadTextures()
{
backgroundTexture = ScreenManager.Content.Load<Texture2D>("Common/woodenbackground");
ballofSteelTexture = ScreenManager.Content.Load<Texture2D>("Samples/ballofsteel");
ballShadowTexture = ScreenManager.Content.Load<Texture2D>("Samples/ballshadow");
woodenForegroundTexture = ScreenManager.Content.Load<Texture2D>("Samples/woodenforeground");
labyrinthShadowTexture = ScreenManager.Content.Load<Texture2D>("Samples/labyrinthshadow");
labyrinthTexture = ScreenManager.Content.Load<Texture2D>("Samples/labyrinth");
}
Then we draw the ball using the preloaded textures:
public override void Draw(GameTime gameTime)
{
BeginSpriteBatch();
DrawBackground();
DrawLabyrinthShadow();
DrawBall();
DrawLabyrinth();
EndSpriteBatch();
base.Draw(gameTime);
}
private void DrawLabyrinth()
{
ScreenManager.SpriteBatch.Draw(labyrinthTexture, ConvertUnits.ToDisplayUnits(labyrinth.Position),
null, Color.Azure, labyrinth.Rotation, labyrinthOrigin, SCALE_FOR_WP_RESOLUTION, SpriteEffects.None, 0f);
}
private void DrawBall()
{
var ballShadowVector = new Vector2(World.Gravity.X, World.Gravity.Y) * ACC_BALL_SHADOW_COEF;
ball.Draw(ballofSteelTexture, ballShadowTexture, ballShadowVector);
}
private void DrawLabyrinthShadow()
{
var shadowVector = new Vector2(World.Gravity.X, World.Gravity.Y) * -ACC_SHADOW_COEF;
ScreenManager.SpriteBatch.Draw(labyrinthShadowTexture, ConvertUnits.ToDisplayUnits(labyrinth.Position),
null, Color.Azure, labyrinth.Rotation, labyrinthOrigin - shadowVector, SCALE_FOR_WP_RESOLUTION, SpriteEffects.None, 0f);
}
private void DrawBackground()
{
ScreenManager.SpriteBatch.Draw(backgroundTexture, screenRectangle, Color.White);
}
private void EndSpriteBatch()
{
ScreenManager.SpriteBatch.End();
}
private void BeginSpriteBatch()
{
ScreenManager.SpriteBatch.Begin(0, null, null, null, null, null, Camera.View);
}
The labyrinth itself is rendered with 3 textures: the wooden background, the labyrinth shadow and the labirinth wooden borders:
As a result, the shadows helps in imitating the 3D look and feel of the labyrinth according to the rotation provided by the accelerometer data:
the labyrinth texture is first loaded much like the steel ball mentioned earlier:
public override void LoadContent()
{
.
.
.
labyrinthTexture = ScreenManager.Content.Load<Texture2D>("Samples/labyrinth");
.
.
.
}
Now we create our labyrinth by joining a series of rectangles, right? Wrong! Unlike the ball,
the labyrinth is created from the texture image of the labyrinth. This is a very nice feature of Farseer engine that
makes our life much easier! The farseer built-in functions create a solid object from our labyrinth plain image, provided
that we leave the empty spaces with the transparent color, like this:
Now imagine how easy it is to create new levels... amazing, isn't it? The Farseer functions will do the hard job for us, and
from the code below, the part that is more worth mentioning is the BayazitDecomposer.ConvexPartition method, which creates
smaller convex polygons out of a single big concave polygon:
public override void LoadContent()
{
.
.
.
uint[] data = new uint[labyrinthTexture.Width * labyrinthTexture.Height];
labyrinthTexture.GetData(data);
Vertices textureVertices = PolygonTools.CreatePolygon(data, labyrinthTexture.Width, false);
Vector2 centroid = -textureVertices.GetCentroid();
textureVertices.Translate(ref centroid);
labyrinthOrigin = -centroid;
textureVertices = SimplifyTools.ReduceByDistance(textureVertices, 4f);
List<Vertices> list = BayazitDecomposer.ConvexPartition(textureVertices);
scale = 2f;
Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(1)) * scale;
foreach (Vertices vertices in list)
{
vertices.Scale(ref vertScale);
}
labyrinth = BodyFactory.CreateCompoundPolygon(World, list, 1f, BodyType.Dynamic);
labyrinth.BodyType = BodyType.Dynamic;
labyrinth.IsStatic = true;
StartAccelerometer();
}
"Haptics Technology", or "Haptics" is the tactile feedback provided by devices to the their users in response to some
event occurring in games or simulations in some kind of virtual reality.
Although this project may not be called "virtual reality", indeed it tries to imitate those old glass-covered wooden labyrinth boxes with little balls inside. The addition of the
tactile feedback certainly improves the user experience and that's why we are using it. But how to implement it in Windows Phone?
One of the nice aspects of programming for smartphones is that this little toys have many features/devices/sensors that desktop computers don't. So, while in desktop programming
you target the keyboard/mouse/screen/speakers/disk, in smartphones you now have access to built-in accelerometer/vibration/gyroscope/multitouch and so on.
We implement the tactile feedback in Windows Phone just by accessing the Microsoft.Devices.VibrateController
class (http://msdn.microsoft.com/en-us/library/microsoft.devices.vibratecontroller(v=vs.92).aspx)
and starting and maintaining the vibration for as long as we want. Simply as that. But when and how long will we vibrate the device?
First, we must handle the collisions between ball and the labyrinth. Then, we should measure the impact of the collision and finally tell the phone to vibrate according to the impact.
The harder the impact, the longer the vibration will be maintained.
As for the implementation, we first use the reference to the Microsoft.Devices
namespace:
using Microsoft.Devices;
Then we declare two constants, one for the maximum interval (in milliseconds) of the vibration and the other for the minimum velocity needed to start the vibration.
private const float MAX_VIBRATION_MILLISECS = 16f;
private const float MIN_DELTAVELOCITY_FOR_VIBRATING_DEVICE = 8f;
Next, we implement the BallCollided
event of our ball instance:
private void InitializeBall()
{
.
.
.
ball.BallCollided += new Labyrinth.Ball.BallCollisionEventHandler(ball_BallCollided);
}
void ball_BallCollided(float deltaVelocity)
{
if (deltaVelocity > MIN_DELTAVELOCITY_FOR_VIBRATING_DEVICE)
{
VibrateController.Default.Start(TimeSpan.FromMilliseconds(
deltaVelocity > MAX_VIBRATION_MILLISECS ? MAX_VIBRATION_MILLISECS : deltaVelocity)
);
}
}
Notice that not all collisions cause vibrations. The ball impact velocity must match a predefined minimum value, as well as the vibration must not
be kept for too long. We have applied these constraints because otherwise the feedback wouldn't be as real as we expected. For example, little impacts
would make the phone vibrate all the time, while big impacts would produce a long vibration. This would be annoying for the user. The best way as I
found it, was to keep the vibrations under these two constraints.
If you tried to develop games before the Windows Phone Mango update, you may have become disappointed by the fact that XNA only managed to to run at
30 frames per second (no matter how powerful your hardware was). This might have been a nice spec 3 years back, but for the current smartphone platforms 30 fps
is terrible for action games.
Fortunately, Mango update lifted this barrier by letting us to enable the highest frame rate possible for our hardware, which is currently 60 fps. But in
order to benefit from this feature, you must first configure yor application to run at 60 fps.
First, implement the PreparingDeviceSettings
event of the GraphicsDeviceManager
in your Game
class:
public class LabyrinthGame : Game
{
private GraphicsDeviceManager _graphics;
public LabyrinthGame()
{
.
.
.
_graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(_graphics_PreparingDeviceSettings);
.
.
.
}
Then, inside the PreparingDeviceSettings
event, set the PresentationInterval
to PresentInterval.One
:
void _graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
e.GraphicsDeviceInformation.PresentationParameters.PresentationInterval = PresentInterval.One;
}
For more information, please refer to "Changed APIs for Windows Phone OS 7.1" documentation:
API |
Windows Phone OS 7.0 behavior |
Windows Phone OS 7.1 behavior |
PresentationParameters.PresentationInterval property |
The XNA Framework on Windows Phone OS 7.0 targeted a refresh rate of 30 Hz, regardless of which PresentationInterval you set. |
The XNA Framework on Windows Phone OS 7.1 supports the native device refresh rate, up to 60 Hz. If you set the
PresentationParameters.PresentationInterval property to PresentInterval.One, then the PresentInterval.One value is used, targeting the native device refresh rate. |
And that's it. Maybe in future updates this will be the defaul setting, but for now remember to enable it in your applications.
Yes, at this point, this is not yet a "labyrinth game", this is just "labyrinth". My intention here is not to create a complete game, just to point
to the direction to it. Now you could create a decent score control, "holes" in the floor, traps, fans, coils, elevators, all sorts of obstacles, and
so on. There is a lot of potential for this to become a very nice game, I think.
From now on, it's up to you to create new apps with Windows Phone accelerometer and/or Farseer Physics Engine. Here are some ideas you might be considering,
for example:
- An Angry Birds-like game
- A Tetris-like game (using accelerometer to position the pieces)
- A flight simulator
- A first person shooter
- A snooker game
- A rocket launching game
- A car racing game
And this is just to name a few. This list I could imagine in just a few seconds. There is certainly a huge potential in these technologies.
We are glad that the Windows Phone SDK 7.1 that followed the "Mango" update has brought lots of new tools for us developers. I'm sure this will help improving both the quantity
and quality of the applications at the Windows Phone Marketplace.
If you're interested in developing new, exciting apps for the Marketplace, I strongly recommend stay in touch with the newest tools and technologies. First, read
the "What's New in the Windows Phone SDK 7.1" paper. Also, download the samples in that page and take a
time to understand how it works.
Keep in mind that there are "only" a little more than 36,000 apps in this one year-old Marketplace, but according to Gartner Group
Windows Phone tends to take a more important position in the next years. Here
resides an nice opportunity for you to study and create great apps for the platform.
- 2011-10-31: Initial version.
- 2011-11-06: Code refactoring. Added: tactile feedback.
- 2011-11-08: Added: "60 frames per second" section.