Step by Step: Managed Casual Game Development

4/7/2010

Microsoft Corporation

February 2007

Summary

Learn how to use the game libraries in the Microsoft® .NET Compact Framework running on a Microsoft Windows Mobile® 5.0 device to render both two dimensions and three dimensions using the Managed DirectX® Mobile libraries. This includes creating a device, rendering sprites, vertices, matrix, and meshes. This HOL will take 1 hour and 30 minutes to complete.

Applies To

Microsoft .NET Compact Framework version 2.0Windows Mobile 5.0Microsoft Visual Studio® 2005Microsoft DirectX 9.0 SDKMicrosoft Direct3D®

The following applications are required to run this HOL:

  • Microsoft Windows® XP Professional

  • DirectX 9.0 SDK
    Download and install DirectX 9.0.

  • Visual Studio 2005
    This HOL requires Visual Studio 2005 Standard, Professional, or Team System Editions. It will not work with any of the Express Editions. If you do not have the correct edition of Visual Studio 2005, find out how you can acquire it from the Visual Studio 2005 Developer Center.

  • Microsoft ActiveSync® 4.0
    ActiveSync 4.0 allows for connectivity between a Windows Mobile powered device and your computer.
    Download and install ActiveSync 4.0.

  • Windows Mobile 5.0 SDKs.
    The Windows Mobile 5.0 SDKs for Pocket PC and Smartphone enable development for Windows Mobile powered devices in Visual Studio 2005.
    Download and install Windows Mobile 5.0 SDK for Pocket PC.
    Download and install Windows Mobile 5.0 SDK for Smartphone.

    Note

    If you used an emulator in a previous HOL, you should do a hard reset of the emulator before starting this HOL. (On the emulator, click File, point to Reset, and then click Hard.)

    Note

    During development, if you receive an error that indicates that the process or file is in use, the program is still running on the emulator, and you must close the program before you can deploy and run a new copy. This error may appear anytime in the HOL that you deploy the emulator.

Introduction

Lab 1: Create a Managed D3DM Device

Lab 2: Create a Spinning Triangle with Managed D3DM

Lab 3: Create a Spinning Mesh

Lab 4: Create a Simple Two-Dimensional Sprite Casual Game

Conclusion

Introduction

This HOL shows you how to use transforms to modify the rendered images to run all resolutions devices offer, including VGA Pocket PCs and Smartphones. By the end of this HOL, you will have a basic understanding about how to use these features when you create device-based casual games.

Managed Direct3D Mobile (Managed D3DM) will reduce the volume of code and increase your productivity. The interfaces are more intuitive--inheriting from the powerful and easy-to-use .NET Compact Framework common types. Managed code also frees you from having to deal with most memory management tasks, such as releasing objects. Managed D3DM is provided as a part of the .NET Compact Framework version 2.0. Managed D3DM runs on top of the native Direct3D Mobile, which is being delivered in all Windows Mobile 5.0 powered devices. At a minimum, each new Windows Mobile 5.0 device ships with software drivers, and some are planned to ship with hardware acceleration.

If you're interested in writing a three-dimensional game, you should work through all of the labs in this HOL. If you're interested in writing a two-dimensional game, you can skip Labs 2 and 3.

In this HOL, you will work with the Treo 700 device, which runs a software D3D driver. You can also experiment with the device emulator, which includes the reference software driver. This platform is too slow to do much, but it is available for investigation.

Lab 1: Create a Managed D3DM Device

The objective of this lab is to create a new managed D3DM device as your first Hello World application. The managed D3DM device is the parent of all graphical objects that are painted in your solution. This lab shows you how to create the new device and run it on a Pocket PC.

In this lab, you will perform the following exercise:

  • Creating a new managed D3DM device

Exercise 1: Creating a New Managed D3DM Device

In this exercise, you will use Visual Studio 2005 to create a blank C# project, write the code to create a device, and then compile and deploy the managed D3DM device to a Pocket PC.

To create new project in Visual Studio 2005

  1. In Visual Studio 2005, click File | New | Project.

  2. On the New Project dialog box, open a blank C# project and select Smart Device | Windows Mobile 5.0 Pocket PC.

  3. On the Project menu, click Add Reference.

  4. Add the following references to the project:

    • Microsoft.WindowsMobile.DirectX
    • System.Drawing
    • System.Windows.Forms
    • System

To create a managed D3DM device

  1. Click Project | Add New Item.

  2. In the Add New Item dialog box, delete all the code in the new class.

  3. Add all of the needed references and class definitions by adding the following code.

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using Microsoft.WindowsMobile.DirectX;
    using Microsoft.WindowsMobile.DirectX.Direct3D;
    
    namespace Microsoft.Samples.MD3DM
    {
        // The main class for this sample
        public class CreateDevice : Form
        {
            // The global variables for this project
            Device device = null;
    
            // CreateDevice here
    
            //InitializeGraphics here
    
            //Render here
    
            //OnPaint here
    
            // OnPaintBackground here
    
            // OnKeyPress here
    
            // Main here
    
        }
    }
    
  4. Replace // CreateDevice here by adding the following code to the CreateDevice class. This code sets the Text and MinimizeBox properties so the form can be closed.

        public CreateDevice()
        {
            // Set the caption
            this.Text = "Hello World";
            this.MinimizeBox = false;
        }
    
  5. Initialize the managed D3DM subsystem by adding InitializeGraphics. Replace //InitializeGraphics here with the following code.

    public bool InitializeGraphics()
        {
            try
            {
                // Set up the D3D parameters
                PresentParameters presentParams = new PresentParameters();
    
                // Causes the display to appear in a window rather than
                // full screen
                presentParams.Windowed = true;
    
                // When a new frame is swapped to the front buffer,
                // the old frame will be discarded
                presentParams.SwapEffect = SwapEffect.Discard;
    
                // The first parameter of 0 indicates that the Device object
                // should use the system's physical adapter. The second parameter 
                // identifies the device type. When working with D3DM, the only 
                // supported device type is the default. The third parameter is a 
                // reference to the current form which will be doing the 
                // rendering.The forth parameter, CreateFlags, allows you to set 
                // various options for the device. The final parameter, 
                // presentParams, contains the details about how the image will 
                // be displayed in the render area, what buffers it will use, and 
                // the formats it will have. 
                device = new Device(0, DeviceType.Default, this,
                    CreateFlags.None, presentParams);
            }
            catch (DirectXException)
            {
                return false;
            }
            return true;
        }
    
  6. Set up the render logic to draw each frame with the color of blue. Replace // Render here with the following code.

    private void Render()
    {
            if (device != null)
            {
                //Clear the back buffer to a blue color 
                device.Clear(ClearFlags.Target, Color.Blue,
                        1.0f, 0);
                //Begin the scene
                device.BeginScene();
    
                // Rendering of the scene objects can happen here
    
                //End the scene
                device.EndScene();
                device.Present();
            }
    }
    
    
  7. Create a game loop by overriding OnPaint. Replace //OnPaint here with the following code.

    protected override void OnPaint(PaintEventArgs e)
        {
            // Render on painting
            this.Render();
    
            // Render again
            this.Invalidate();
        }
    
  8. Do not include any logic here because you are filling the entire back buffer with blue in the Render function. Add the following code to your class.

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // Do nothing to ensure that the rendering area is not overdrawn
    }
    
  9. Add OnKeyPress to detect when the user presses the ESC to exit the application. Replace //OnKeyPress here with the following code.

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            // Esc was pressed
            if ((int)(byte)e.KeyChar == (int)Keys.Escape)
            this.Close();
        }
    
  10. Create Main to launch the application. Replace //Main here with the following code.

        static void Main()
        {
            CreateDevice frm = new CreateDevice();
    
            // Initialize Direct3D
            if (!frm.InitializeGraphics())
            {
                MessageBox.Show("Could not initialize Direct3D. " +
                    "This tutorial will exit.");
                return;
            }
    
            Application.Run(frm);
        }
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher. You should see the blue managed D3DM device with the title Hello World.

  5. On the managed D3DM device, click OK to close it.

Summary

In this lab, you performed the following exercise:

  • Creating a new managed D3DM device

Lab 2: Create a Spinning Triangle with Managed D3DM

The objective of this lab is to create a simple object, a triangle, from a vertex buffer, render the object, and then make it rotate by using the Hello World application as a starting point.

In this lab, you will perform the following exercises:

  • Creating a triangle
  • Changing the triangle's perspective

Exercise 1: Creating a Triangle

This exercise uses the skeleton managed D3DM device that you created in the first lab to draw a triangle with three vertices. Triangles are the building blocks of all D3D drawings; given enough triangles, you can draw anything.

To modify the Hello World code to draw a triangle

  1. Modify the global variables in the CreateDevice class to include a VertexBuffer variable. Replace // The global variables for this project with the following code.

    VertexBuffer vertexBuffer = null;
    PresentParameters presentParams = new PresentParameters();
    
  2. Change the form text in the CreateDevice function. Replace this.Text = "Hello World"; with the following code.

            this.Text = "Spinning Tringle";
    
  3. Modify the code in InitializeGraphics by replacing all of the code in the try block with the following code.

    presentParams.Windowed = true;
    presentParams.SwapEffect = SwapEffect.Discard;
    device = new Device(0, DeviceType.Default, this,
                        CreateFlags.None, presentParams);
    device.DeviceReset += new EventHandler(this.OnResetDevice);
    this.OnCreateDevice(device, null);
    this.OnResetDevice(device, null);
    
  4. Add OnCreateDevice to the CreateDevice class to configure the device on creation, as shown in the following code.

        void OnCreateDevice(object sender, EventArgs e)
        {
            Pool vertexBufferPool;
            Caps caps;
            Device dev = (Device)sender;
            // Get the device capabilities
            caps = dev.DeviceCaps;
    
            if (caps.SurfaceCaps.SupportsVidVertexBuffer)
                vertexBufferPool = Pool.VideoMemory;
            else
                vertexBufferPool = Pool.SystemMemory;
    
            // Now create the VertexBuffer
            vertexBuffer = new VertexBuffer(
                typeof(CustomVertex.PositionColored), 3, dev, 0,
                CustomVertex.PositionColored.Format, vertexBufferPool);
            vertexBuffer.Created += new EventHandler(
                this.OnCreateVertexBuffer);
            this.OnCreateVertexBuffer(vertexBuffer, null);
        }
    
  5. Add OnResetDevice to configure the device on reset, as shown in the following code.

        void OnResetDevice(object sender, EventArgs e)
        {
            Device dev = (Device)sender;
            // Turn off culling, so you see the front and back of the triangle
            dev.RenderState.CullMode = Cull.None;
            // Turn off D3D lighting because you are providing your own vertex
            // colors
            dev.RenderState.Lighting = false;
    }
    
  6. Add OnCreateVertexBuffer to configure the triangle when the buffer is created or recreated, as shown in the following code.

        void OnCreateVertexBuffer(object sender, EventArgs e)
        {
            VertexBuffer vb = (VertexBuffer)sender;
            CustomVertex.PositionColored[] verts =
                 (CustomVertex.PositionColored[])vb.Lock(0, 0);
            verts[0].X = -1.0f;
            verts[0].Y = -1.0f;
            verts[0].Z = 0.0f;
            verts[0].Color = System.Drawing.Color.DarkGoldenrod.ToArgb();
    
            verts[1].X = 1.0f;
            verts[1].Y = -1.0f;
            verts[1].Z = 0.0f;
            verts[1].Color = System.Drawing.Color.MediumOrchid.ToArgb();
    
            verts[2].X = 0.0f;
            verts[2].Y = 1.0f;
            verts[2].Z = 0.0f;
            verts[2].Color = System.Drawing.Color.Cornsilk.ToArgb();
            vb.Unlock();
    }
    
  7. Modify Render to draw the TriangleList. Replace // Rendering of the scene objects can happen here with the following code.

    device.SetStreamSource(0, vertexBuffer, 0);
    device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher. You should see the blue managed D3DM device with a triangle drawn in the center of it.

  5. On the managed D3DM device, click OK to close it.

Exercise 2: Changing the Triangle's Perspective

In this exercise, you will add a view matrix to change the triangle's perspective, so the triangle appears to rotate.

To change perspective in code by using matrices

  1. Create SetupMatrices by setting up the world view, and then by changing that view each frame, as shown in the following code.

    private void SetupMatrices()
         {
            // For the world matrix, you will rotate the object about
            // the y-axis. Set up the rotation matrix to generate 1 full 
            // rotation (2*PI radians) every 1000 ms. To avoid the loss of
            // precision inherent in very high floating point numbers, the
            // system time is modulated by the rotation period before
            // conversion to a radian angle.
            int iTime = Environment.TickCount % 1000;
            float fAngle = iTime * (2.0f * (float)Math.PI) / 1000.0f;
            device.Transform.World = Matrix.RotationY(fAngle);
    
            // Set up the view matrix. A view matrix can be defined given
            // an eye point, a point to look at, and a direction for which
            // way is up. In this code, you set the eye five units back along the
            // z-axis and up three units, look at the origin, and define
            // "up" to be in the y-direction.
            device.Transform.View = Matrix.LookAtLH(
                new Vector3(0.0f, 3.0f, -5.0f),
                new Vector3(0.0f, 0.0f, 0.0f),
                new Vector3(0.0f, 1.0f, 0.0f));
    
            // For the projection matrix, you set up a perspective transform
            // (which transforms geometry from three-dimensional view space to 
            // two-dimensional viewport
            // space, with a perspective divide that makes objects smaller in
            // the distance). To build a perpsective transform, you need the
            // field of view (1/4 pi is common),
            // the aspect ratio, and the near and far clipping planes
            // (which define at what distances geometry should be no longer
            // be rendered).
            device.Transform.Projection =
                Matrix.PerspectiveFovLH((float)Math.PI / 4,
                1.0f, 1.0f, 100.0f);
          }
    
  2. Call SetupMatrices, as shown in the following code, from the Render function by adding a call to it just after the BeingScene.

    SetupMatrices();
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher. You should see the blue managed D3DM device with a triangle drawn in the center of it.

  5. On the managed D3DM device, click OK to close it.

Summary

In this lab, you performed the following exercises:

  • Creating a triangle
  • Changing the triangle's perspective

Lab 3: Create a Spinning Mesh

Three-dimensional modeling software can represent an object by using complicated geometry. Data representing this geometry is called a mesh and is commonly stored in a Direct3D .x file. Direct3D uses meshes to load the objects from these files. This lab introduces the topic of meshes and shows how to load, render, and unload a mesh. Managed D3DM uses meshes in a file format that is a subset of the .x format with the extension .md3dm. A mesh contains the data for a complex model. It is an abstract data container that contains resources, such as textures and materials, and attributes, such as position data and adjacency data.

Lab Objective

The objective of this lab is to show you how to convert an .x file, load that mesh on a device, and change the perspective to make it appear to spin.

In this lab, you will perform the following exercises:

  • Converting an .x file to an .md3dm file
  • Loading a mesh onto a device
  • Creating a Spinning Tiger Project

Note

This lab has a weakness in the OnResetDevice method, where resources are loaded from the assembly. If the assembly name and the default namespace is not Lab3; it will fail. If you fail to run the tiger, check the default namespace by opening the project's properties (click Project | Properties). The Application tab includes the default namespace. If the value is not Lab3, change it.

Exercise 1: Converting an .x File to an .md3dm File

Visual Studio 2005 includes a sample that converts .x files to .md3dm files. You will use the application that you build from that sample to create an .md3dm file.

To convert an .x file to an .md3dm file

  1. Open a Command Prompt window (click Start | Run, and then type cmd).

  2. Type cd c:.

  3. Type cd C:\HOL320\Tools\.

  4. Type MeshConvertercs.exe Tiger.x Tiger.md3dm to convert the Tiger.x file to Tiger.md3dm.

To set up a new Pocket PC project

  1. In Visual Studio 2005, click File | New | Project.

  2. In the New Project dialog box, open a new C# project, and then select SmartDevices | Windows Mobile 5.0 Pocket PC.

  3. Add an empty project, and name it Lab3. (The project name is referenced in the code.)

  4. On the Project menu, click Add Reference.

  5. Add the following references:

    • Microsoft.WindowsMobile.DirectX
    • System.Drawing
    • System.Windows.Forms
    • System

To embed the Tiger.md3dm file and Tiger.bmp file as resources in your project

  1. Click Project | Add Existing Item.

  2. Click Add Resource | Add Existing File.

  3. Change Files of Type to All Files.

  4. Change the Look In value to C:\HOL320\Tools\.

  5. Add Tiger.md3dm and Tiger.bmp (holding down SHIFT and clicking the left mouse button selects multiple files).

  6. Get the properties of each resource by looking in Solution Explorer, right-clicking the resource, and then choosing Properties.

  7. Change the Build Action value to Embedded Resource to compile these resources into the assembly.

  8. Click Project | Add Existing Item.

  9. Browse to C:\HOL320\Tools\Create Project, and add MeshLoader.cs. MeshLoader.cs is a part of the Mesh sample and includes the code to load an .md3dm mesh.

Exercise 2: Loading a Mesh onto a Device

This exercise leads you through loading the mesh.

To add a new class

  1. Click Project | Add New Item.

  2. On the Add New Item dialog box, add a new code file.

To add a class definition

  1. Add all of the needed references and class definitions by adding the following code:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using Microsoft.WindowsMobile.DirectX;
    using Microsoft.WindowsMobile.DirectX.Direct3D;
    using System.Reflection;
    
  2. Add a namespace and the class definitions by adding the following code:

    namespace Microsoft.Samples.MD3DM
    {
        public class Meshes : Form
        {
            // The D3D rendering device
            Device device = null;
    
            // The mesh object in the system
            Mesh mesh = null;
    
            // Materials for the mesh
            Material[] meshMaterials;
    
            // Textures for the mesh
            Texture[] meshTextures;
    
            // The display parameters for drawing a D3D window
            PresentParameters presentParams = new PresentParameters();
    
            //InitializeGraphics here
    
    //Add OnDeviceReset
    
    //SetupMatrices here
    
    //Render here
    
    //OnPaint here
    
    //OnPaintBackGround here
    
    //OnKeyPress here
    
    //OnResize here
    
    //Main here
        }
    }
    

Exercise 3: Creating a Spinning Tiger Project

This exercise shows you how to change the perspective of the Tiger project to make it appear to spin.

To add code to the project and render it

  1. Replace //InitializeGraphics here by adding the following code to the InitializeGraphics method in the Meshes class.

    bool InitializeGraphics()
         {
            try
            {
                // Draw the graphics inside a standard window
                presentParams.Windowed = true;
    
                // Discard the current frame when drawing a new one
                presentParams.SwapEffect = SwapEffect.Discard;
    
                // Create a 16-bit depth buffer 
                presentParams.EnableAutoDepthStencil = true;
                presentParams.AutoDepthStencilFormat = DepthFormat.D16;
    
                // Create the D3D device
                device = new Device(0, DeviceType.Default, this,
                    CreateFlags.None, presentParams);
                device.DeviceReset += new System.EventHandler(
                    this.OnResetDevice);
                this.OnResetDevice(device, null);
            }
            catch (Exception)
            {
                return false;
            }
    
            return true;
    }
    
  2. Replace //Add OnDeviceReset by adding the following code.

    Note

    GetManifestResourceStream starts with the path to the default namespace. Project1 is referenced as the default. Check the default namespace property before completing this step.

    void OnResetDevice(object sender, EventArgs e)
        {
            string[] textureFilenames = null;
    
            Device dev = (Device)sender;
    
            // Turn on the zbuffer
            dev.RenderState.ZBufferEnable = true;
    
            // Turn on ambient lighting 
            dev.RenderState.Ambient = System.Drawing.Color.White;
    
            // Turn on perspective correction for textures.
            // This provides a more accurate visual at the cost
            // of a small performance overhead
            dev.RenderState.TexturePerspective = true;
    
            // Load the mesh from the specified file.
            //Note Project1 refers to the defalut namespace
            mesh = MeshLoader.LoadMesh(device,
                Assembly.GetExecutingAssembly().GetManifestResourceStream(
                "Lab3.Tiger.md3dm"), MeshFlags.SystemMemory,
                out meshMaterials, out textureFilenames);
    
    
            // Extract the material properties and texture names
            meshTextures = new Texture[meshMaterials.Length];
            for (int i = 0; i < meshMaterials.Length; i++)
            {
    
                // Set the ambient color for the material
                // (D3DX does not do this)
                meshMaterials[i].Ambient = meshMaterials[i].Diffuse;
    
                // Create the texture
                meshTextures[i] = TextureLoader.FromStream(dev,
                        Assembly.GetExecutingAssembly().GetManifestResourceStream(
                        "Lab3." + textureFilenames[i]));
    }
    }
    
  3. Replace //SetupMatrices here by adding the following code.

    void SetupMatrices()
        {
            // For the world matrix, you will just leave it as the identity
            device.Transform.World =
                Matrix.RotationY(Environment.TickCount / 1000.0f);
    
            // Set up the view matrix. A view matrix can be defined given
            // an eye point, a point to look at, and a direction for which
            // way is up. In this code, you set the eye five units back along the
            // z-axis and up three units, look at the origin, and define
            // "up" to be in the y-direction.
            device.Transform.View = Matrix.LookAtLH(
                new Vector3(0.0f, 2.0f, -3.0f),
                new Vector3(0.0f, 0.0f, 0.0f),
                new Vector3(0.0f, 1.0f, 0.0f));
    
            // For the projection matrix, you set up a perspective
            // transform (which transforms geometry from three-dimensional view 
            // space to two-dimensional
            // viewport space, with a perspective divide that makes objects 
            // smaller in the distance). To build a perpsective transform,
            // you need the field of view (1/4 pi is common), the aspect
            // ratio, and the near and far clipping planes (which define at
            // what distances geometry should be no longer be rendered).
            device.Transform.Projection =
                Matrix.PerspectiveFovLH((float)(Math.PI / 4), 1.0f, 1.0f,
                100.0f);
    }
    
  4. Replace //Render here with the following code.

    private void Render()
         {
            if (device == null)
                return;
    
            //Clear the back buffer to a blue color 
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer,
                System.Drawing.Color.Blue, 1.0f, 0);
            //Begin the scene
            device.BeginScene();
            // Set up the world, view, and projection matrices
            SetupMatrices();
    
            // Meshes are divided into subsets; one for each material.
            // Render them in a loop
            for (int i = 0; i < meshMaterials.Length; i++)
            {
                // Set the material and texture for this subset
                device.Material = meshMaterials[i];
                device.SetTexture(0, meshTextures[i]);
    
                // Draw the mesh subset
                mesh.DrawSubset(i);
            }
    
            //End the scene
            device.EndScene();
            device.Present();
    }
    
  5. Replace //OnPaint here with the following code.

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            // Render the mesh to the screen
            Render();
    
            // Invalidating the window will cause it to be redrawn in
            // the future
            Invalidate();
    }
    
  6. Replace //OnPaintBackGround here with the following code.

        protected override void OnPaintBackground(
            System.Windows.Forms.PaintEventArgs e)
        {
            // Doing nothing ensures the background will never overdraw
            // the previous rendering
    }
    
  7. Replace //OnKeyPress here with the following code.

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            // If ESC was pressed, then shut down
            if ((int)e.KeyChar == (int) Keys.Escape)
                this.Close();
    }
    
  8. Replace //OnResize here with the following code.

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
    }
    
  9. Replace //Main here with the following code.

    static void Main()
        {
            Meshes meshForm = new Meshes();
    
            // Initialize Direct3D
            if (!meshForm.InitializeGraphics())
            {
                MessageBox.Show("Could not initialize Direct3D. " +
                    "This tutorial will exit.");
                return;
            }
    
            // Run the form
            try
            {
                Application.Run(meshForm);
            }
            catch (Exception e)
            {
                MessageBox.Show("An error occured and this sample needs " +
                    " to  close:" + e.Message);
            }
    }
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher.

  5. On the managed D3DM device, click OK to close it.

Summary

In this lab, you performed the following exercises:

  • Converting an .x file to an .md3dm file
  • Loading a mesh onto a device
  • Creating a Spinning Tiger Project

Lab 4: Create a Simple Two-Dimensional Sprite Casual Game

Traditional two-dimensional casual games are based on animated sprites. Managed D3DM offers a Sprite class that allows you render and rotate sprites.

Managed D3DM handles sprites as textures. These textures get loaded then displayed on the managed D3DM device through the Sprite class. The Sprite class takes parameters to allow different areas of the texture to be rendered. This lab refers to these areas as tiles. Moving from area to area renders the animations of the sprite, causing it to appear to rotate, as shown in Figure 1. The Sprite class also places the sprite on the managed D3DM device, thus allowing it to move from frame to frame. Finally sprites can be transformed to allow the texture to be modified, in this example, scaled to fit the screen.

Bb278108.1cb51177-0c6d-41a0-9823-f9b715d4503f(en-us,MSDN.10).gif

Figure 1. Donut.bmp appears to be rotating

Lab Objective

The objective of this lab is to render a static sprite, animate a sprite, move the sprite on the screen and detect collisions, and scale the sprite according to the device's screen.

In this lab, you will perform the following exercises:

  • Rendering a static sprite
  • Animating a sprite
  • Moving a sprite
  • Detecting collisions
  • Scaling a sprite according to screen size

Exercise 1: Rendering a Static Sprite

This exercise will lead you through rendering a static sprite, including creating the project, constructing the data class, and rendering one of tiles of the sprite.

To create a new project

  1. In Visual Studio 2005, click File | New | Project.

  2. In the New Project dialog box, open a new C# project, and then select SmartDevices | Windows Mobile 5.0 Pocket PC.

  3. Name the project Lab4.

  4. On the Project menu, click Add Reference.

  5. Add the following references:

    • Microsoft.WindowsMobile.DirectX
    • System.Drawing
    • System.Windows.Forms
    • System
  6. Click Project | Add Existing Item.

  7. Add donuts.bmp to the project. (Select Files of Type Image Files, and then locate file at C:\HOL320\Image.)

  8. Get the properties of each resource by looking in Solution Explorer, right-clicking the resource, and then choosing Properties.

  9. Change the Build Action value to Embedded Resource to compile donuts.bmp into the assembly.

The sprite data is stored in the TileSet class.

To add the data class TileSet

  1. Click Project | Add New Item.

  2. In the Add New Item dialog box, add a new class.

  3. Name the new class TileSet.cs.

  4. Delete all of the code in the new class.

  5. Add the following code to the new class.

    using System;
    using Microsoft.WindowsMobile.DirectX;
    using Microsoft.WindowsMobile.DirectX.Direct3D;
    
    namespace SpriteLab
    {
        public class TileSet
        {
            private Texture texture;
            //Returns the sprite texture
            public Texture Texture
            {
                get
                {
                    return texture;
                }
            }
            private int xOrigin;
            //Returns the x starting location
            public int XOrigin
            {
                get
                {
                    return xOrigin;
                }
            }
            private int yOrigin;
            //Returns the y starting location
            public int YOrigin
            {
                get
                {
                    return yOrigin;
                }
            }
            private int numberFrameRows;
            //Returns the number of frames in a row
            public int NumberFrameRows
            {
                get
                {
                    return numberFrameRows;
                }
            }
            private int numberFrameColumns;
            //Returns the number of frames in a column
            public int NumberFrameColumns
            {
                get
                {
                    return numberFrameColumns;
                }
            }
            private int xExtent;
            //Returns the x coordinate for the center of the sprite 
            public int ExtentX
            {
                get
                {
                    return xExtent;
                }
            }
    
            private int yExtent;
            //Returns the y coordinate for the center of the sprite 
            public int ExtentY
            {
                get
                {
                    return yExtent;
                }
            }
    
            //Class constructor
            public TileSet(Texture tex, int StartX, int StartY, int RowCount, int ColumnCount, int xWidth, int yHeight)
            {
                xOrigin = StartX;
                yOrigin = StartY;
                xExtent = xWidth;
                yExtent = yHeight;
                numberFrameRows = RowCount;
                numberFrameColumns = ColumnCount;
                texture = tex;
            }
        }
    }
    

To create the SpriteLab class code that will render one sprite

  1. Click Project | Add New Item.

  2. On the Add New Item dialog box, add a new code file.

  3. Name the new class SpriteLab.cs.

  4. Add the following code to the new class.

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using Microsoft.WindowsMobile.DirectX;
    using Microsoft.WindowsMobile.DirectX.Direct3D;
    using System.Reflection;
    
    namespace SpriteLab
    {
        // The main class for this sample
        public class CreateDevice : Form
        {
            // Global variables for this project
            Device device = null;   //MD3DM Device
            Texture texture = null; //Sprite bmp
            Rectangle tilePosition; //Location of tile
            TileSet tileSet = null; //Local TileSet
            Sprite sprite;          //Sprite Varible 
            Int16 countX = 0;       //Count to Track Columes of Tiles
            Int16 countY = 0;       //Count to Track Rows of Tiles
            Vector3 spritePosition = new Vector3(50, 50, 0); //Start Location of Sprite
            Vector3 spriteCenter = new Vector3(0, 0, 0);     //Sprite Center, 0,0,0 
            Vector2 spriteVelocity = new Vector2(1, 1);      // x,y velocity
      float scale = 1;            //Configure Scale for screen size
    
      //Implementation here
    
    }
    }
    
  5. Under //Implementation here, add the following code.

    public CreateDevice()
            {
                // Set the caption
                this.Text = "Sprite Sample";
                this.MinimizeBox = false;
    // Add Key Event Handler here
            }
    
  6. Under the CreateDevice class, add the following code.

    public bool InitializeGraphics()
            {
                try
                {
                    // Set up the D3D parameters
                    PresentParameters presentParams = new PresentParameters();
    
                    // Causes the display to appear in a window rather than
                    // full screen
                    presentParams.Windowed = true;
    
                    // When a new frame is swapped to the front buffer,
                    // the old frame will be discarded
                    presentParams.SwapEffect = SwapEffect.Discard;
    
                // The first parameter of 0 indicates that the Device object
                // should use the system's physical adapter. The second parameter 
                // identifies the device type. When working with D3DM, the only 
                // supported device type is the default. The third parameter is a 
                // reference to the current form which will be doing the 
                // rendering.The forth parameter, CreateFlags, allows you to set 
                // various options for the device. The final parameter, 
                // presentParams, contains the details about how the image will 
                // be displayed in the render area, what buffers it will use, and 
                // the formats it will have. 
              device = new Device(0, DeviceType.Default, this,
    CreateFlags.None, presentParams);
                    device.DeviceReset += new EventHandler(
                        this.OnResetDevice);
                    this.OnResetDevice(device, null);
                }
                catch (DirectXException)
                {
                    return false;
                }
                return true;
            }
    
  7. Under InitializeGraphics, add the following code.

    void OnResetDevice(object sender, EventArgs e)
            {
                Device dev = (Device)sender;
                //Create sprite object on device
                sprite = new Sprite(dev);
                //Get the donuts resource out of the assembly and assign to
                //texture
                //Default parameters for TextureLoader.FromStream 
    //(device, stream,D3DX.Default, D3DX.Default, D3DX.Default, 
    // D3DX.Default, Usage.None, Format.Unknown,
    // Pool.VideoMemory,(Filter)D3DX.Default, (Filter)D3DX.Default, 0)
                //Load texture from the resource, and set the color black as the
                //alpha blended color
                texture = TextureLoader.FromStream(dev,
    Assembly.GetExecutingAssembly()
    .GetManifestResourceStream("Lab4.donuts.bmp"), 
    D3DX.Default, D3DX.Default, D3DX.Default, D3DX.Default, 
    Usage.None, Format.Unknown, Pool.VideoMemory, (Filter)D3DX.Default, (Filter)D3DX.Default, Color.Black.ToArgb());
                //Configure TileSet to the size of tile in bmp
                tileSet = new TileSet(texture, 0, 0, 6, 5, 32, 32);
                //Set tilePosition including the offset to the center of the 
                //sprite
                tilePosition = new Rectangle(tileSet.XOrigin, 
    tileSet.YOrigin, tileSet.ExtentX * 2, tileSet.ExtentY * 2);
    
                //Add a call to Calcualte Scale here
            }
    
  8. Add the following code to render the sprite.

    private void Render()
            {
    
                //Clear the back buffer to a blue color 
                device.Clear(ClearFlags.Target, System.Drawing.Color.BlueViolet,
                    1.0f, 0);
    
                //Begin the scene
                device.BeginScene();
    
                sprite.Begin(SpriteFlags.AlphaBlend);
    
    //Add transform here 
    
    
                //Draw a sprite at the current position 
                sprite.Draw(tileSet.Texture, tilePosition, 
    spriteCenter, spritePosition, Color.White.ToArgb());
                sprite.End();
    
    
                //End the scene
                device.EndScene();
                device.Present();
    
                //rotateSprite code here
    
                //moveSprite code here
    
        }
    
  9. Add the following code for OnPaint, OnPointBackground and Main, which finishes the sample.

    // Called to repaint the window
            protected override void OnPaint(PaintEventArgs e)
            {
                // Render on painting
                this.Render();
    
                // Render again
                this.Invalidate();
            }
    
            // Called to repaint the window's background
            protected override void OnPaintBackground(PaintEventArgs e)
            {
                // Do nothing to ensure that the rendering area is not overdrawn
            }
    
    
            static void Main()
            {
                CreateDevice frm = new CreateDevice();
    
                // Initialize Direct3D
                if (!frm.InitializeGraphics())
                {
                    MessageBox.Show("Could not initialize Direct3D. " +
                        "This tutorial will exit.");
                    return;
                }
    
                Application.Run(frm);
            }
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher.

  5. On the managed D3DM device, click OK to close it.

Exercise 2: Animating a Sprite

In this exercise, you will move from tile to tile with each render loop, which makes the donut appear to spin.

To rotate the sprite

  1. Add the following rotateSprite function above the Main function.

     private void rotateSprite()
            {
                //Change the count to move to the next sprite tile
                if (++countX > 4)
                {
                    countX = 0;
                    if (++countY > 5)
                    {
                        countY = 0;
                    }
                }
                //Set the coordinates for the current tile
                tilePosition.X = tileSet.XOrigin + (countX * tileSet.ExtentX * 2);
                tilePosition.Y = tileSet.YOrigin + (countY * tileSet.ExtentY * 2);
            }
    
  2. Replace //rotateSprite code here with the following code to call rotateSprite from the Render function.

    rotateSprite();
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher.

  5. On the managed D3DM device, click OK to close it.

Exercise 3: Moving a Sprite

In this exercise, you will move the sprite around the screen. You will move the sprite location based on an x and y velocity set by using the direction keys.

To move the sprite

  1. Above the Main function, add the following function to move the sprite.

            private void moveSprite()
            {
    
                //Add collision logic here
    
                //Advance sprite by adding to its position the velocity            
                spritePosition.X += spriteVelocity.X;
                spritePosition.Y += spriteVelocity.Y;
    
    //Add calculate sprite center here
    
            }
    
  2. Replace //moveSprite code here with the following code to add the moveSprite function to the Render function.

    moveSprite();
    
  3. Above the Main function, add the following code to change the spriteVelocity by using the navigation pad.

    private void CreateDevice_KeyDown(object sender, KeyEventArgs e)
            {
                //Use rocker to modify the velocity 
                if ((e.KeyCode == System.Windows.Forms.Keys.Up))
                {
                    // Rocker Up
                    // Up
                    spriteVelocity.Y -= 1;
                }
                if ((e.KeyCode == System.Windows.Forms.Keys.Down))
                {
                    // Rocker Down
                    // Down
                    spriteVelocity.Y += 1;
    
                }
                if ((e.KeyCode == System.Windows.Forms.Keys.Left))
                {
                    // Left
                    spriteVelocity.X -= 1;
                }
                if ((e.KeyCode == System.Windows.Forms.Keys.Right))
                {
                    // Right
                    spriteVelocity.X += 1 + 1;
    
                }
    
      }
    
  4. Replace // Add Key Event Handler here with the following code to add the navigation pad event handler to the CreateDevice function.

    this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.CreateDevice_KeyDown);
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher. You should see the blue managed D3DM device with the rotating donut. You can use the navigation pad to move it around.

  5. On the managed D3DM device, click OK to close it.

Exercise 4: Detecting Collisions

You will notice from the last procedure that the donut moves off the screen and keeps going. In this exercise, you add will code to detect collisions with walls and change the velocity of the donut.

To detect walls and change the velocity by 90 degrees

  • Replace //Add collision logic here with the following collision code to add it to the moveSprite function.

                //Simple collision detection with end of screen.
    
                if ((spritePosition.X * scale) > (this.Width - (tileSet.ExtentX * 2 *      scale)) || (spritePosition.X * scale) < 0)
                {
                    //If the sprite hits either the left or right side of screen, 
                    //provide the reciprocal velocity   
                    spriteVelocity.X *= -1;
                }
    
    
                if ((spritePosition.Y * scale) > (this.Height - (tileSet.ExtentY * 2 * scale)) || (spritePosition.Y * scale) < 0)
                {
                    //If the sprite hits either the top or bottom of the screen, 
                    //provide the reciprocal velocity     
                    spriteVelocity.Y *= -1;
    
                }
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher. You should see the blue managed D3DM device with the rotating donut. You can use the navigation pad to move the donut around.

  5. On the managed D3DM device, click OK to close it.

Exercise 5: Scaling a Sprite According to Screen Size

The donut image is created for a VGA screen with a screen resolution of 640 x 480. But not all devices have a VGA resolution. In this exercise, you will use a transform to modify the size of the donut according to the screen size.

To scale the donut to the current screen

  1. Above the Main function, add the calculateScale function.

            private float calculateScale()
            {
                //Donunts.bmp is drawn for a VGA resolution. If the device is not
                //VGA resolution, provide a scale to modify the image.
                float vgaWidth = 480;
    
                return this.Width / vgaWidth;
            }
    
  2. Replace //Add call to calculate scale here with the following code.

    scale = calculateScale();
    
  3. Replace //Add calculate sprite center here with the following code. Transforms need the sprite center calculated.

                spriteCenter.X = spritePosition.X + tileSet.ExtentX;
                spriteCenter.Y = spritePosition.Y + tileSet.ExtentY;
    
  4. Replace //Add transform here with the following code to add the transform to the Render function.

    sprite.Transform = Matrix.Translation(spriteCenter) * Matrix.Scaling(scale, scale, 1.0f);
    

To compile and deploy the managed D3DM project

  1. Press F5 to compile and run the application. If any compilation issues occur, you need to fix them.

  2. On the Deploy dialog box, select Windows Mobile 5.0 PocketPC Device.

  3. Click Deploy. Visual Studio deploys the project.

  4. Click Yes when the device prompts you to accept the project from an unknown publisher. You should see the blue managed D3DM device with the rotating donut. You can use the navigation pad to move it around.

  5. On the managed D3DM device, click OK to close it.

Conclusion

In this lab, you performed the following exercises:

  • Rendering a static sprite
  • Animating a sprite
  • Moving a sprite
  • Detecting collisions
  • Scaling a sprite according to screen size