Over a million developers have joined DZone.

Building a Cross-Platform Game on PC, Xbox, and WP7 with XNA Framework Game Studio

· Mobile Zone

Visually compose APIs with easy-to-use tooling. Learn how IBM API Connect provides near-universal access to data and services both on-premises and in the cloud, brought to you in partnership with IBM.

In today’s development world being able to target multiple platforms from the same game codebase is a rarity. Often times the same game project needs be re-written to run on a different platform causing a lot of headache, frustration and lost time. This is where XNA Game Studio comes in. XNA Game Studio is a set of tools which Microsoft has built that makes the process of developing a game easy and fun. The true beauty of XNA Game Studio is in its ability to seamlessly provide developers a way to build for multiple platforms. In this tutorial we are going to look at how we can build a simple XNA Framework game which runs on Windows Phone 7, Xbox and the PC by using a unified code base.

What 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);

            #endif
17.  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?

The Mobile Zone is brought to you in partnership with Strongloop and IBM.  Visually compose APIs with easy-to-use tooling. Learn how IBM API Connect provides near-universal access to data and services both on-premises and in the cloud.

Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}