XNA is one of the two APIs that’s supported on Windows Phone 7 (the other being Silverlight), so a natural thing to do is to port an already existing XNA game (running on Windows or XBOX 360) to the new Windows Phone 7 platform. I wanted to see how easy (or maybe not so easy) it would be to do the actual porting.
My starting point is a relatively simple 2D game, the one developed throughout the XNA 2D tutorial I did a some months back. We’ll start with the final game project (discussed in the last part in that series), and try to do as little work as possible to make it run on a Windows Phone 7 device.
Let’s begin!
A typical XNA project has 2 actual Visual Studio projects: one is the game code we write and the other one contains the game “assets”: textures, audio files, fonts, models, etc. This is more efficient and sharable than using a single project for everything (which was the case with pervious XNA versions prior to version 4).
The first step (after opening the solution in Visual Studio) is to “copy” the project with the actual code to the Windows Phone 7 platform settings. Fortunately, this is easy to do in Visual Studio, by right-clicking the project and selecting “Create Copy of Project for Windows Phone 7”:

(this actually works for converting to XBOX 360, as can be seen from the image).
The result is a third project, that uses the same source files as the original. Note that these files are not copies, but the actual files (it’s a bit confusing as there is no “shortcut” icon on those files). This means any change I make would also affect the original game project, so some care is necessary. The new project defines the symbol WINDOWS_PHONE, that can be used to distinguish phone specific code using #if blocks, much the same way as it’s done with XBOX 360 vs. Windows (XBOX symbol is defined):

One example is the Main method. In WP7 XNA, it has no purpose. XNA creates an instance of the first Game-derived class it finds. So, Main is unnecessary (but not harmful, as it’s just a method). It’s automatically excluded:
namespace AlienRaid {
#if WINDOWS || XBOX
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args) {
using(AlienRaidGame game = new AlienRaidGame()) {
game.Run();
}
}
}
#endif
}
Let’s Build!
Now let’s build the project. Unfortunately, we get some compilation errors. It turns out, that the Windows Phone SDK uses the C# 3.0 compiler, so that C# 4.0 features are unavailable. I’ve used optional parameters in the original game, so they don’t compile. We have to remove those and return to the “classic” pre-C# 4.0 way of overloading methods/constructors with fewer arguments, that call the primary method/constructor. Adding #if/#else/#endif for this feature seems overkill.
Now we can finally run the new project. The emulator starts up, and after rotating it sideways, it looks like this:

It may be difficult to see, but the starfield does not occupy the entire screen space. Perhaps this is due to the different resolution of Windows Phone. WP7 mandates a resolution of 480x800 (technically, 480x320 may also be supported, but no current device has this resolution). The original game uses 800x600, so the ratio is different (4/3 in the original game, 5/3 in WP7).
Let’s change that:
#if WINDOWS_PHONE
graphics.PreferredBackBufferHeight = 480;
graphics.PreferredBackBufferWidth = 800;
#else
graphics.PreferredBackBufferHeight = 600;
graphics.PreferredBackBufferWidth = 800;
#endif
If we run it now, we strangely see no noticeable change!
This seems weird indeed, so let’s break with the debugger in the code where a new star is born:
if((star.Position.Y += star.Velocity.Y) > height) {
// "generate" a new star
star.Position = new Vector2(_rnd.Next(Game.Window.ClientBounds.Width), -_rnd.Next(20));
star.Velocity.Y = (float)_rnd.NextDouble() * 5 + 2;
star.Color = new Color(_rnd.Next(256), _rnd.Next(256), _rnd.Next(256), 128);
}
Looking at Game.Window.ClientBounds.Width, we find that it’s equal to 480 and Height is equal to 728! (Where did 728 came from? we’ll discuss this shortly. For now pretend it returns 800). Just the other way around!
So maybe we should just switch the values:
#if WINDOWS_PHONE
graphics.PreferredBackBufferHeight = 800;
graphics.PreferredBackBufferWidth = 480;
#else
The result is less that satisfying. The phone seems to be “stuck” in portrait mode, and rotating it (the emulator or the phone) seems to make the stars go sideways… oops.

So, how can we solve that? The previous state was definitely better. Maybe we should revert to that and just set the correct width and height based on the platform and not the Game.Window.ClientBounds property.
Let’s add two properties, WindowWidth and WindowHeight to the AlienRaidGame class and initialize them based on platform:
public int WindowWidth { get; private set; }
public int WindowHeight { get; private set; }
This is done when setting the preferred width & height:
#if WINDOWS_PHONE
graphics.PreferredBackBufferHeight = WindowHeight = 480;
graphics.PreferredBackBufferWidth = WindowWidth = 800;
#else
graphics.PreferredBackBufferHeight = WindowHeight = 600;
graphics.PreferredBackBufferWidth = WindowWidth = 800;
#endif
Now we need to replace all occurrences of Game.Window.ClientBounds.Width/Height with those properties. A few solution wide search & replace does the trick. Now it looks much better:

By the way, changing the GraphicsDeviceManager.SupportedOrientations property does not help with this issue.
Now, where did that 728 came from? Why not 800? Where did that 72 go?
The 72 pixels are reserved for the “status” of WP7, which shows the battery state, wi-fi connectivity, etc. If you want to take that space as well, just set the game to “full screen mode”. This is what it means in the context of the Windows Phone device:
graphics.IsFullScreen = true;
This gives the entire 800 pixels at your disposal.
What’s Next?
Now that the game seems “oriented”, let’s play. But, wait… how do we do that? The screen says “Press ENTER to play”. Oops, no ENTER on Windows Phone… no real keyboard.
We need to change that. But that will be left for the next part.