XNA 4.0 On Windows Phone 7 Using BasicEffect
Jan 16, 2011
14 minute read

This post will provide a quick overview of basic topics regarding XNA 4.0 games and Windows Phone 7 (wp7). At the end of this tutorial, you’ll be using 3d graphics to draw a colored cube on the windows phone emulator.

For the duration of the post, I will assume that you have the Windows Phone Developer Tools RTW installed and have a copy of Visual Studio 2010 around; I’m using VS 2010 Professional. I also assume that you have some programming experience with C# and .NET. It will also help to have some knowledge of XNA 4.0 or a book handy because I only give the most cursory explanations possible.

Lets get started!

working with the emulator

The first step will be to open Visual Studio 2010 and create a new project. There are many project wizards available, each tailored to one of the three XNA platforms: Windows, XBox 360 and Windows Phone 7. For this tutorial, create a Windows Phone Game.

new project

Once you create the project based on the wizard, you get a solution with two C# projects: one for content to link into the game and another project for the game itself. The meat of the generated code can be found in the Game1.cs file:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace BasicPhoneGame
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here

            base.Draw(gameTime);
        }
    }
}

Have the Debug configuration selected (which it is by default) and build the solution. After it finishes building, run the game in the wp7 emulator. When you choose to Start Debugging, you’ll see the emulator load up.

loading emu

After a few seconds, your game will load.

wizard run

That’s right, the project wizard creates a game that consists of a background drawn in cornflower blue. Not very exciting.

If you click the back button (in the lower left corner of the emulator) or the start button (the windows icon button), the emulator will take you back to the start page of the phone. If you don’t know how a real Windows Phone 7 phone works, this part is a little confusing. The only things shown here are items that are “pinned” … which, when you first start, is only Internet Explorer. You must click the arrow near the top right corner to get the full list of programs.

back click

The full list of applications on the emulator is pretty short! You can see the wizard-created program at the top of the list.

forward click

Clicking BasicPhoneGame will restart the game and show the cornflower blue background.

creating content in Blender

It is possible to create content procedurally with XNA. In fact, a lot of tutorials start out with that method to draw a 3d cube since the number of vertices is relatively small and it illustrates the principles of the work going on “behind the scenes” in XNA.

This method of typing in coordinates for vertices doesn’t scale very well however, and eventually you will likely want to use a tool to create your content to add to your programs. One such tool that XNA supports is Blender, since Blender is able to export Autodesk .fbx files. [A full list of supported content importers is here.] For this tutorial, I will use Blender 2.56 beta, but any reasonably recent version should do just fine.

Start up the Blender.

starting up blender

Without going into too much detail, this shows the default scene that Blender starts out with: a cube, a light and a camera. Lets delete it! Press ‘a’ once to deselect everything and then press ‘a’ again to select everything. When objects in blender are selected you should see a color highlight around them - in this version, that color is orange. Press the ‘delete’ key to delete all the objects. Click on Delete in the window that pops up to confirm the deletion. Your scene should now be empty.

The little icon that looks like a life preserver is the ‘cursor’ in Blender. When you add objects, Blender uses this cursor to determine the new objects initial position.

3d cursor

If you left-click in the Blender window, you move this 3d cursor around which will cause new objects to not be added at the 3d origin. Of the many ways to center the 3d cursor, one is to press ‘shift+c’.

To add a new mesh, you can use the ‘Add’ menu at the top and choose ‘Mesh’ and then ‘Cube’.

add mesh

This creates a basic cube with a scale of [1, 1, 1] and dimensions of [2, 2, 2]. If you want to see all of the properties you can click ‘n’. This is also a good way to make sure it’s position is at (0, 0, 0). If you lost the selection on the cube, you can press ‘a’ until it’s selected again, or you can right-click on the cube to select it.

This is the extent of our modeling for this tutorial. If you want to substitue another shape, go ahead and do so now because the next step is exporting the data into a file format that XNA can understand.

Click the File menu at the top, choose Export and Autodesk FBX. Blender will then give you its version of a file-save dialog box. Choose to save it as ‘Cube.fbx’ in the content project directory (e.g. BasicPhoneGameContent).

export fbx

This is all we need of Blender for this tutorial. If this has sparked your interest at all, there are many great websites, resources and training videos out there. I encourage you to check them out.

code overview

Now that we’ve seen the results of the wizard and made a 3d model, lets go back and quickly review the code. There are five overrides on the base Game class that the wizard generates:

If you’re familiar with game or graphics programming, your intuition will be able to give you good insight into what those methods do. For this basic tutorial, we will modify LoadContent and Draw to eventually draw our cube on the screen.

Lets start by adding in our 3d model. As mentioned above, you should have exported the model to the content project folder. While this is probably not strictly necessary, it does make it more logically consistent with what the wizard generated for you. Add the content by right-clicking the content project and select ‘Add’ and then ‘Existing Item…’

![add model][img10sm]

With the file added to the project in the solution, add a private member of type Model called cubeModel to the Game1 class.

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private Model cubeModel;            //** NEW

Looking inside the LoadContent method, you can see the only thing it does right now is create a new SpriteBatch. Add the line of code shown below to actually load the model.

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load the model
            cubeModel = Content.Load<Model>("Cube");

The Load method is passed a string that will locate the resource to load, in this case our Cube.fbx file. Note that you do not have to supply an extension to this file. Also, you can create sub-directories in the game content project and then find content in these sub-directories by using a double-slash (”\”) as a divider. For example, if I created a “models” folder in the content project and moved the Cube.fbx into it, the “models\Cube” string can be passed to Load.

In order to see the loaded model in the game, we need to setup a view and projection Matrix objects. You can read an article about views and projections here and there are many more available on the Internet. Matrix.CreatePerspectiveFieldOfView and Matrix.CreateLookAt make setting up these matrices easy.

            // Make the projection matrix
            float aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
                                (float)graphics.GraphicsDevice.Viewport.Height;
            float fov = MathHelper.PiOver4 * aspectRatio * 3 / 4;
            Matrix projectionMatrix = Matrix.CreatePerspectiveFieldOfView(fov, aspectRatio, 5f, 1000f);

            // Setup the camera vectors objects and view matrix
            Vector3 camPos = new Vector3(0.0f, 0.9f, 15.0f);
            Vector3 camView = new Vector3(0.0f, 0.9f, -5.0f);
            Vector3 camUp = new Vector3(0.0f, 1.0f, 0.0f);
            Matrix viewMatrix = Matrix.CreateLookAt(camPos, camView, camUp);

For this tutorial, you don’t need a deep understanding of the matrix math, because BasicEffect will take care of the work for you. Speaking of BasicEffect, we need to set one up here.

Define the basicShader member at the top of the class.

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private Model cubeModel;            //** NEW
        private BasicEffect basicShader;    //** NEW

Then create the object and initialize it in the LoadContent method right after the camera work we just did.

            // Initialize the basic shader
            basicShader = new BasicEffect(graphics.GraphicsDevice);
            basicShader.Alpha = 1.0f;
            basicShader.LightingEnabled = true;
            basicShader.DiffuseColor = new Vector3(0.95f, 0.0f, 0.0f);
            basicShader.SpecularColor = new Vector3(0.25f, 0.25f, 0.25f);
            basicShader.SpecularPower = 5.0f;
            basicShader.AmbientLightColor = new Vector3(0.70f, 0.70f, 0.70f);
            basicShader.World = Matrix.Identity;
            basicShader.View = viewMatrix;
            basicShader.Projection = projectionMatrix;

If you’re familiar with graphics programming, these settings have obvious meanings. We set the View and Projection properties once here, making a static camera. The world matrix is simply the identity matrix. The color the shader will use is bright red with some specular highlights and a decent amount ambient light.

You may wonder: Why use a built-in effect when I could have used a custom shader specific to my needs? The answer is because the Windows Phone target does not support custom shaders; only built-in shaders are supported. That sounds like a bad thing at first, but these effects are really configurable and do a lot of things.

With the model loaded, the camera set and the BasicEffect shader initialized, we are done loading our content for the sample. All that remains is to draw the cube!

Define a new function called DrawSampleModel:

        private void DrawSampleModel(Model m)
        {
            if (m == null)
                return;

            // only one part in the model
            ModelMesh mesh = m.Meshes[0];
            ModelMeshPart meshPart = mesh.MeshParts[0];

            // set the vertex source to the mesh part's vertex buffer @ offset
            graphics.GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer, meshPart.VertexOffset);

            // set the index buffer to the mesh part's
            graphics.GraphicsDevice.Indices = meshPart.IndexBuffer;

            // Set a new world position
            Matrix scale = Matrix.CreateScale(4f, 4f, 4f);
            Matrix rot = Matrix.CreateRotationY((float)Math.PI / 8);
            basicShader.World = Matrix.Identity * scale * rot;

            foreach (EffectPass effectPass in basicShader.CurrentTechnique.Passes)
            {
                effectPass.Apply();
                graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                                              0,
                                                              0,
                                                              meshPart.NumVertices,
                                                              meshPart.StartIndex,
                                                              meshPart.PrimitiveCount);
            }
        }

What does this function do?

It takes the first, and only, ModelMeshPart in the Model and hooks it into the graphics device by setting its VertexBuffer and IndexBuffer. [An older article explaining these buffers can be found here.] The world matrix is set using the ISROT (Identity, Scale, Rotate, Orbit, Translate) sequence. Finally, for each EffectPass it draws the primitive.

All that’s left now is to call this new method from within Draw.

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            DrawSampleModel(cubeModel); //** New

            base.Draw(gameTime);
        }

Now, we’re ready! Build and run the program in the emulator. You should see something like this:

cube model in phone

The entire source code for the modified Game1.cs file is here:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace BasicPhoneGame
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private Model cubeModel;            //** NEW
        private BasicEffect basicShader;    //** NEW

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load the model
            cubeModel = Content.Load<Model>("Cube");

            // Make the projection matrix
            float aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
                                (float)graphics.GraphicsDevice.Viewport.Height;
            float fov = MathHelper.PiOver4 * aspectRatio * 3 / 4;
            Matrix projectionMatrix = Matrix.CreatePerspectiveFieldOfView(fov, aspectRatio, 5f, 1000f);

            // Setup the camera vectors objects and view matrix
            Vector3 camPos = new Vector3(0.0f, 0.9f, 15.0f);
            Vector3 camView = new Vector3(0.0f, 0.9f, -5.0f);
            Vector3 camUp = new Vector3(0.0f, 1.0f, 0.0f);
            Matrix viewMatrix = Matrix.CreateLookAt(camPos, camView, camUp);

            // Initialize the basic shader
            basicShader = new BasicEffect(graphics.GraphicsDevice);
            basicShader.Alpha = 1.0f;
            basicShader.LightingEnabled = true;
            basicShader.DiffuseColor = new Vector3(0.95f, 0.0f, 0.0f);
            basicShader.SpecularColor = new Vector3(0.25f, 0.25f, 0.25f);
            basicShader.SpecularPower = 5.0f;
            basicShader.AmbientLightColor = new Vector3(0.70f, 0.70f, 0.70f);
            basicShader.World = Matrix.Identity;
            basicShader.View = viewMatrix;
            basicShader.Projection = projectionMatrix;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            DrawSampleModel(cubeModel);

            base.Draw(gameTime);
        }

        private void DrawSampleModel(Model m)
        {
            if (m == null)
                return;

            // only one part in the model
            ModelMesh mesh = m.Meshes[0];
            ModelMeshPart meshPart = mesh.MeshParts[0];

            // set the vertex source to the mesh part's vertex buffer @ offset
            graphics.GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer, meshPart.VertexOffset);

            // set the index buffer to the mesh part's
            graphics.GraphicsDevice.Indices = meshPart.IndexBuffer;

            // Set a new world position
            Matrix scale = Matrix.CreateScale(4f, 4f, 4f);
            Matrix rot = Matrix.CreateRotationY((float)Math.PI / 8);
            basicShader.World = Matrix.Identity * scale * rot;

            foreach (EffectPass effectPass in basicShader.CurrentTechnique.Passes)
            {
                effectPass.Apply();
                graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                                              0,
                                                              0,
                                                              meshPart.NumVertices,
                                                              meshPart.StartIndex,
                                                              meshPart.PrimitiveCount);
            }
        }
    }
}

If you liked how easy it is to work with 3d graphics in XNA, there are a lot of resources for you to learn more. However, a lot of material out there was written for versions previous to XNA 4.0, which introduced a lot of breaking changes. Just be mindful of that.

Here are a few links I found useful when learning XNA:

I also want to mention that I found the “Microsoft XNA Game Studio Creator’s Guide” second edition book very helpful. Of the XNA books I’ve seen, this was by far the one that made the most sense to me as a 3d graphics neophyte. Unfortunately it’s not for XNA 4.0 which causes some problems when trying to type in examples exactly as found in the book. The core concepts are mostly the same.

It should also be mentioned that App Hub has a huge amount of sample code available (here and here). One particular example I looked at a lot when writing this tutorial was bounce.

And finally, you can also download Petzold’s wp7 book for free, legally, from Microsoft here. It covers many topics of wp7 programming, though it is light on details of working with XNA.