Managed Sprite Sample and 2D Game Whitepapers for Devices

We on the NETCF team have been busy completing our v2 product, which has kept me away from my BLOG.  I have found some time to get you all up to speed on whats been happening in the Managed Mobile gaming space.

We had four great articles published describing how to create 2D games using Winforms.  These are based on games developed by the NETCF team as part of our application building exercise we run throughout the product cycle.  

Pocket Jack: Writing a Card-Playing Application
Summary:
This article describes how to create and use a card game for Smartphones. The description is illustrated by the development of a blackjack game for Smartphone.
https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/pocketjack.asp

Focus Point: An Image Scaling Game for Smartphones
Summary:
This article discusses how to manage image animation, scaling, and loading on Smartphones. The description is illustrated by the development of an image scaling game for Smartphone.
https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/focuspoint.asp

Pocket Bots: Writing a Battle Game for Smartphones
Summary:
This article describes how to generate randomly tiled landscapes, fire projectiles, detect collisions, and perform frame-based animation on Smartphones. A battle game illustrates how to develop these features for Smartphones.
https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/pocketbots.asp

StarLight: Writing a Space-Shooter Game for Smartphones
Summary:
This article describes how to generate moving star fields, animated sprites, and scripted movements for game objects. A space shooter game illustrates how to develop these features for Smartphones.

https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/starlight.asp

I’ve been porting the code samples from Beginning .NET Game Programming in C# by David Weller to the PocketPC to fully understand how you all will port your games from PC to the device.  Recently I’ve been working through his Sprite sample, which leads me to a slightly different sample for the pocketpc.  My sample uses the donut bmp which contains the sprite animation which I loop through to animate.  Next I give the sprite an x and y velocity to move it across the screen.  I detect collision with the end of the screen, once detected, I reverse said velocity.  Finally use the dpad to modify the x and y velocity.  Simple, but hope it illustrates using Sprites on device.  

Currently I’m using a pre-release build for the Dell Axim x50v, which includes hardware acceleration! More information on the x50v running Windows Mobile 5.0 see Dells x50v page.  It runs my sample quite well.  Unfortunately it has still not been released by Dell, so for the time being, you will have to be content with the emulator shipping in VS 2005 in Beta2 and in the latest CTP’s.  D3D performance with the emulator is very slow. It ships with the reference driver so it runs, ok more like walks slowly.

A few interesting observations before we get into the code.  Setting an alpha color on a bmp is not as easy as it seems.  The alpha color can be set in the TextureLoader.FromStream, colorKey parameter with a load of other parameters most of which you will want to set to their default.  Unfortunately our documentation does not include the defaults so I will list them here:

Parameter Default
readBytes D3DX.Default
width D3DX.Default
height D3DX.Default
mipLevels D3DX.Default
usage Usage.None
format Format.Unknown
pool Pool.VideoMemory
filter (Filter)D3DX.Default
mipFilter (Filter)D3DX.Default
colorKey 0

Once I identified the defaults to TextureLoader.FromSteam it was not too difficult to complete the sample using the MD3DM samples included in VS.

If you have Visual Studio 2005 Beta2 (or one of the CTP’s) and the Windows Mobile 5.0 SDK installed you can build my sample by following my steps below.

1. Create new blank PocketPC project
2. Add references to Microsoft.WindowsMobile.DirectX, System.Drawing, System.Windows.Forms
3. From the PPC SDK add the donuts.bmp to the project and set its Build Action to Embedded Resource to included it in the assembly
4. Add a new class named TitleSet which will contain data describing the sprite titles. Add code:

 //---------------------------------------------------------------------
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------
 using System;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;
namespace SpaceDonuts {
   public class TileSet {
      private Texture texture;

        public Texture Texture {
            get {
               return texture;
         }
       }
       private int xOrigin;

        public int XOrigin {
            get {
               return xOrigin;
         }
       }
       private int yOrigin;

        public int YOrigin {
            get {
               return yOrigin;
         }
       } 
      private int numberFrameRows;

        public int NumberFrameRows {
            get {
               return numberFrameRows;
         }
       }
       private int numberFrameColumns;

     public int NumberFrameColumns {
         get {
               return numberFrameColumns;
          }
       }
       private int xExtent;

        public int ExtentX {
            get {
               return xExtent;
         }
       }

       private int yExtent;
        public int ExtentY {
            get {
               return yExtent;
         }
       }

       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;
      }
   }
}

5. Next add a new code file and add the game code below:
//---------------------------------------------------------------------//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A//PARTICULAR PURPOSE.//---------------------------------------------------------------------

 using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;
using System.Runtime.InteropServices;
using System.Reflection;

namespace SpaceDonuts
{
    // The main class for this sample
    public class CreateDevice : Form
    {
        // Our global variables for this project
        Device device = null;
        Texture texture = null;
        Rectangle tilePosition;
        TileSet tileSet = null;
        Sprite sprite;
        Int16 countX = 0;
        Int16 countY = 0;
        Vector3 spritePosition = new Vector3(200, 200, 0);
        Vector3 spriteCenter = new Vector3(16, 16, 0);
        int spriteVelocityX = 1;
        int spriteVelocityY = 1;
        

        public CreateDevice()
        {
            // Set the caption
            this.Text = "Sprite Sample";
            this.MinimizeBox = false;
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.CreateDevice_KeyDown);
        }

        // Prepare the rendering device
        public bool InitializeGraphics()
        {
            try
            {
                // Now let's setup our 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;

                // 0 gives us the first monitor adapter on the system
                // for md3dm you must use the default device type
                // this refers to the form which will provide the rendering
                // area createflags allows various options to be set
                // present parameters set various details of how the image 
                // will be displayed within render area and what buffers and
                // formats it will have
                device = new Device(0, DeviceType.Default, this, CreateFlags.None, presentParams);
                device.DeviceReset += new System.EventHandler(
                      this.OnResetDevice);
                this.OnResetDevice(device, null);
            }
            catch (DirectXException)
            {
                return false;
            }
            return true;
        }

        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("Sprites.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);
        }

        // All rendering for each frame occurs here
        private void Render()
        {

                //Clear the backbuffer to a blue color 
                device.Clear(ClearFlags.Target, System.Drawing.Color.BlueViolet,
                    1.0f, 0);

                //Begin the scene
                device.BeginScene();

                sprite.Begin(SpriteFlags.AlphaBlend);
                //draw Sprite at current position. 
                sprite.Draw(tileSet.Texture, tilePosition, spriteCenter, spritePosition,Color.White.ToArgb());
                sprite.End();

             
                //End the scene
                device.EndScene();
                device.Present();
                
                //rotateSprite moves image to the next tile
                rotateSprite();
                //moveSprite to change the sprite position for the next render.
                moveSprite();

                //Application.DoEvents();
         }
        

        // Called to repaint the window
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            // Render on painting
            this.Render();

            // Render again
            this.Invalidate();
        }

        // Called to repaint the window background
        protected override void OnPaintBackground(
            System.Windows.Forms.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);
        }

        private void CreateDevice_KeyDown(object sender, KeyEventArgs e)
        {
            //use rocker to modify velocity 
            if ((e.KeyCode == System.Windows.Forms.Keys.Up))
            {
                // Rocker Up
                // Up
                spriteVelocityY -= 1;
            }
            if ((e.KeyCode == System.Windows.Forms.Keys.Down))
            {
                // Rocker Down
                // Down
                spriteVelocityY += 1;
                
            }
            if ((e.KeyCode == System.Windows.Forms.Keys.Left))
            {
                // Left
                spriteVelocityX -= 1;
            }
            if ((e.KeyCode == System.Windows.Forms.Keys.Right))
            {
                // Right
                spriteVelocityX += 1;
                
            }

        }

        private void moveSprite()
        {
            if (spritePosition.X > (this.Width - (tileSet.ExtentX * 2)) || spritePosition.X < 0)
            {
                //if sprite hits either left or right side of screen provide reciprocal velocity   
                spriteVelocityX *= -1;
            }


            if (spritePosition.Y > (this.Height - tileSet.ExtentY * 2) || spritePosition.Y < 0)
            {
                //if sprite hits either top or bottom of screen provide reciprocal velocity     
                spriteVelocityY *= -1;
     
            }

            //advance sprite by adding to its position the velocity            
            spritePosition.X += spriteVelocityX;
            spritePosition.Y += spriteVelocityY;
        }

        private void rotateSprite()
        {
            //change the count to move to the next sprite tile
            if (++countX > 4)
            {
                countX = 0;
                if (++countY > 5)
                {
                    countY = 0;
                }
            }
            //set coodinates for current tile
            tilePosition.X = tileSet.XOrigin + (countX * tileSet.ExtentX * 2);
            tilePosition.Y = tileSet.YOrigin + (countY * tileSet.ExtentY * 2);
        }


    }
}

6. Compile and deploy to the emulator or device if you have it.  Move the donut around the screen with the d-pad, watch it hit the side of the screen and bounce off.