Building a Cross-Platform Game on PC, Xbox, and WP7 with XNA Framework Game Studio
Join the DZone community and get the full member experience.
Join For FreeWhat you’ll need
First off, if you haven’t already done so, please visit http://create.msdn.com/ and find your way to
the free download of Visual Studio 2010 and XNA Game Studio 4.0. Follow the
installation instructions.
What we will build
We are going to build an ultra-simple XNA Framework game with the main purpose of demonstrating how to deploy to multiple platforms. The game will essentially be moving a golf ball into a target. On Windows Phone 7 the ball will be controlled using the accelerometer, on the Xbox it will be moved using the thumb stick on the controller and on the computer it will be moved using the arrow keys on the keyboard. When the user moves the golf ball into the target the game resets. Simple!
Step 1: Build Windows Phone 7 XNA Framework Game
We’ll begin by creating a new Windows XNA Framework project which we will then modify to run in Windows Phone 7 and the Xbox. Go to File -> New Project. Under XNA Framework Game Studio 4.0, select a “Windows Phone Game (4.0)” and title it “CrossPlatformXNA FrameworkGame.”
Let’s begin with a bit of the theory…
Next, we need to begin coding our project. Let me begin by briefly describing how the XNA Framework works at a higher level. The chart below depicts the flow of an XNA Game Studio application at a high level.
The code begins by entering the constructor, initializing variables, loading any content to the ContentManager and then finally entering a game loop. The game loop is where a majority of the code will exist. The game loop runs as often as we would like it to. By default, a Windows Phone 7 XNA Framework application runs at 30 frames per second. This means that the game loop is called 30 times per second. The game loop is actually composed of two functions named Update() and Draw().
Let’s import some textures to our project…
So before we begin coding lets import some textures that we can use for our game. I have created textures for the Golf Ball and the Target textures that you can use in your project. There are two resolutions for each texture. The reason for this is that depending on what platform you are running your game on, the resolution may vary. Thus I have provided two images; one is for high definition resolution on the Xbox and the other is a lower resolution version for the Windows Phone and PC.
Note: Both Windows Phone 7 and Xbox include a hardware scaler that can upscale or downscale images on demand in hardware with excellent results. Relying on the hardware scaler can reduce game size by having just one set of content.
Save the files to your computer and then go to the Solution Explorer in Visual Studio 2010 and right click your CrossPlatformXNA FrameworkGameContent Project and select Add -> Existing Item. Select the images you saved to your computer.
Getting down to the code…
So now we understand at a very high level how an XNA Framework application runs and we actually have some textures that we can use to build our application, we can begin to code.
1. Begin by renaming Game1.cs inside of the CrossPlatformXNA FrameworkGame project to “GolfGameMain.cs”
2. Open GolfGameMain.cs and define the following variables which will be used for our two textures and also location and velocity information.
// Textures Texture2D ball; Texture2D target; // Location information Vector2 ballPt; Vector2 targetPt; // normalized velocity vector Vector2 ballVelocity; // Used to multiply on the velocity vector float velocityMultiplier;
3. Inside of the Initialize() method add the following line of code to set the speed of our golf ball
velocityMultiplier = 13.0f;
4. Write a function called ResetGame() that initializes the velocity and position variables. The method should look like the following. We will position the ball to initially be slightly to the right of the top left corner and the target to be slightly to the left of the bottom right corner. The velocity of the ball will start at zero.
private void ResetGame() { ballVelocity = Vector2.Zero; ballPt = new Vector2(target.Width, target.Height); targetPt = new Vector2(graphics.GraphicsDevice.Viewport.Width - 2*target.Width, graphics.GraphicsDevice.Viewport.Height - 2*target.Height); }
5. Load the textures we imported to the ContentManager by adding the following lines of code to the LoadContent() method. After we have loaded our content we can call ResetGame() to position the objects.
ball = Content.Load<Texture2D>("ball_small"); target = Content.Load<Texture2D>("target_small"); ResetGame();
6. Now that we have loaded our content to the content manager let’s draw the textures we loaded to the screen based on the position that was have pictures. Modify the Draw() method to look like the following.
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); spriteBatch.Begin(); // Draw our objects to the screen spriteBatch.Draw(target, targetPt, Color.White); spriteBatch.Draw(ball, ballPt, Color.White); spriteBatch.End(); base.Draw(gameTime); }
7. At this point we should be able to run the program and see the golf ball and the target drawn to screen. It should look like the image below.
8. The next step would be to add a method for doing collision checking. The method should handle when the golf ball is completely contained within the target. If it is, then we will return true and otherwise return false.
private bool CheckForCollision() { Rectangle ballRect = new Rectangle((int)ballPt.X, (int)ballPt.Y, ball.Width, ball.Height); Rectangle targetRect = new Rectangle((int)targetPt.X, (int)targetPt.Y, target.Width, target.Height); return targetRect.Contains(ballRect); }
9. Now that we have the logic for collision detection we can add the logic for moving the golf ball to the target. We can accomplish this by adding the following inside of the Update() method. Once we detect a collision we can reset the game by calling the ResetGame() function we wrote earlier.
KeyboardState kState = Keyboard.GetState(PlayerIndex.One); Keys[] pressedKeys = kState.GetPressedKeys(); ballVelocity = Vector2.Zero; foreach (Keys k in pressedKeys) switch (k) { case Keys.Right: ballVelocity.X = velocityMultiplier; break; case Keys.Left: ballVelocity.X = -velocityMultiplier; break; case Keys.Down: ballVelocity.Y = velocityMultiplier; break; case Keys.Up: ballVelocity.Y = -velocityMultiplier; break; } ballPt += ballVelocity; if (CheckForCollision()) ResetGame();
10. Give the game a test run. Now you should be able to move the golf ball using the arrow controls on your keyboard. When you move it to the target it should reset the game. Good job. You’ve done a majority of the work. From here on out we will modify the code to support Windows Phone 7 and the Xbox platform. J
Step 2: Modify the code to support Windows Phone 7
11. Create a copy of the CrossPlatformXNA FrameworkGame project for Windows Phone 7. To do this right-click the project name and click “Create copy of project for Windows Phone.” A second project should be created and will have the title “Windows Phone 7 copy of CrossPlatformXNA FrameworkGame.”
12. Add a reference to your project by right clicking on “References -> Add Reference” as shown below. Then select Microsoft.Devices.Sensors and click Add.

13. Now close all files that may be open and from the Windows Phone 7 copy of Cross PlatformXNA FrameworkGame select and open GolfGameMain.cs.
14. Add the following nameplace to the top of the project. Wrap the namespace import by the macro “#if WINDOWS_PHONE”. This tells the compiler to disregard the code within the “#if…#endif” unless it is being compiled for Windows Phone 7. We will do the same thing later for the Xbox by using “#if XBOX… #endif”.
// Used for accelerometer data from Windows Phone #if WINDOWS_PHONE using Microsoft.Devices.Sensors; #endif
15. Add a class level variable to add support for the accelerometer
#if WINDOWS_PHONE Accelerometer accelerometer; #endif
16. In the constructor add the following code to help change settings dealing with how the game is displayed and what resolution the game runs in.
#if WINDOWS_PHONE // Hides the battery and signal strength bar graphics.IsFullScreen = true; #else // Code to support changing the resolution of the game for the Xbox to handle high def screens graphics.IsFullScreen = false; graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings); #endif17. Modify the Initialize() function which you wrote earlier to initialize the accelerometer and have different speeds for the golf ball depending on the platform.
#if WINDOWS_PHONE if (accelerometer == null) { // Instantiate the accelerometer sensor accelerometer = new Accelerometer(); accelerometer.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged); accelerometer.Start(); } velocityMultiplier = 10.0f; #elif WINDOWS velocityMultiplier = 13.0f; #else velocityMultiplier = 20.0f; #endif
18. Add a function to handle the event we are subscribing to in the previous step. This function will handle the accelerometer reading changes. From this we will set the golf ball’s velocity. This is the equivalent code to handling the different key presses in the Windows version.
#if WINDOWS_PHONE void accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e) { ballVelocity.X = -velocityMultiplier*(float)e.Y; ballVelocity.Y = -velocityMultiplier*(float)e.X; } #endif
19. Modify the LoadContent() method to load different textures depending on what platform we are targeting. For Windows Phone and Windows we will load the lower resolution images and for the Xbox we will load the high resolution images.
// Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // Load our game content to the ContentManager #if WINDOWS_PHONE || WINDOWS ball = Content.Load<Texture2D>("ball_small"); target = Content.Load<Texture2D>("target_small"); #else ball = Content.Load<Texture2D>("ball"); target = Content.Load<Texture2D>("target"); #endif ResetGame();
20. Modify the Update() function so that we only look at keyboard input if we are compiling for the Windows version of the game.
#if WINDOWS KeyboardState kState = Keyboard.GetState(PlayerIndex.One); Keys[] pressedKeys = kState.GetPressedKeys(); ballVelocity = Vector2.Zero; foreach (Keys k in pressedKeys) switch (k) { case Keys.Right: ballVelocity.X = velocityMultiplier; break; case Keys.Left: ballVelocity.X = -velocityMultiplier; break; case Keys.Down: ballVelocity.Y = velocityMultiplier; break; case Keys.Up: ballVelocity.Y = -velocityMultiplier; break; } #endif
21. Great! Now right click on “Windows Phone 7 copy of CrossPlatformXNA FrameworkGame” in the Solutions Explorer and run “Debug -> Run New Instance.” It should bring up the Windows Phone 7 emulator just as shown below. Sadly you will not be able to test the functionality of the accelerometer unless you deploy to a physical Windows Phone 7.

Almost there! Let’s finish off with creating an Xbox version.
22. Create a copy of the CrossPlatformXNA FrameworkGame project for Xbox. To do this right-click the project name and click “Create copy of project for Xbox 360” A third project should be created and will have the title “Xbox 360 copy of CrossPlatformXNA FrameworkGame.”
23. Modify the constructor one last time to add an event handler for setting the resolution of the game. The final constructor should look like this.
public GolfGame() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; // Frame rate is 30 fps by default for Windows Phone. TargetElapsedTime = TimeSpan.FromTicks(333333); #if WINDOWS_PHONE // Hides the battery and signal strength bar graphics.IsFullScreen = true; #else // Code to support changing the resolution of the game for the Xbox to handle high def screens graphics.IsFullScreen = false; graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings); #endif }
24. Now we must define the function definition for the event we subscribed to.
void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e) { #if XBOX foreach (Microsoft.Xna.Framework.Graphics.DisplayMode displayMode in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes) { // High def resolution support for Xbox (if available) if (displayMode.Width == 1920 || displayMode.Width == 1280) { e.GraphicsDeviceInformation.PresentationParameters.BackBufferFormat = displayMode.Format; e.GraphicsDeviceInformation.PresentationParameters.BackBufferHeight = displayMode.Height; e.GraphicsDeviceInformation.PresentationParameters.BackBufferWidth = displayMode.Width; } } #endif }
25. Now we must modify the Update() method to add support for the Xbox controller. The final version of the function will look like this.
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); Vector2 velocity = Vector2.Zero; #if WINDOWS KeyboardState kState = Keyboard.GetState(PlayerIndex.One); Keys[] pressedKeys = kState.GetPressedKeys(); ballVelocity = Vector2.Zero; foreach (Keys k in pressedKeys) switch (k) { case Keys.Right: ballVelocity.X = velocityMultiplier; break; case Keys.Left: ballVelocity.X = -velocityMultiplier; break; case Keys.Down: ballVelocity.Y = velocityMultiplier; break; case Keys.Up: ballVelocity.Y = -velocityMultiplier; break; } #elif XBOX ballVelocity.X = velocityMultiplier * GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.X; ballVelocity.Y = -velocityMultiplier * GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.Y; #endif ballPt += ballVelocity; if (CheckForCollision()) ResetGame(); base.Update(gameTime); }
26. That’s it! We’ve written an app that runs on all 3 platforms! Phew! Wasn’t that simple?
Opinions expressed by DZone contributors are their own.
Comments