Export (0) Print
Expand All

Writing Mobile Games Using the Microsoft .NET Compact Framework

.NET Compact Framework 1.0
 

Ravi Krishnaswamy
Microsoft Corporation

April 2003

Applies to:
    Microsoft® .NET Compact Framework 1.0
    Microsoft Visual Studio® .NET 2003

Summary: Learn how to create .NET Compact Framework-based games. Learn the key requirements for writing games targeting small devices and see that the .NET Compact Framework can handle them with ease. Include advanced performance-tuning techniques that you can use to push the limits of your game.(24 printed pages)

Download GoalieGame.msi.


Contents

Introduction
Full Screen Game Form
Overriding OnPaint and OnPaintBackground
Off Screen Bitmap Technique For Painting
Sprites
Image as an Embedded Resource
Tuning up your Paint Method
Dirty Area Computation
Collision Detection
Sprite Velocity
Game Progression Techniques
Optimizing Startup Time
Game Buttons
Other Tidbits
Conclusion
More Information

Introduction

The Microsoft® .NET Compact Framework is a subset of the full Microsoft .NET Framework. The full .NET Framework was factored down to fit resource-constrained devices without compromising user scenarios in such a way that the developers would get enhanced performance with majority of the functionality intact at a much-reduced size.

The benefits of targeting the .NET Compact Framework, among other things, includes single binary deployment for Pocket PC and other Windows CE .NET devices, increased developer productivity and reduced time to market.

In this whitepaper, I will discuss the key requirements for writing games targeting small devices and show that the .NET Compact Framework can handle them with ease. I will also discuss some of the advanced performance-tuning techniques that you can use to push the limits of your game. In short, you will see how easy it is to develop and optimize your games using the .NET Compact Framework. It will be a fun ride, so buckle up and enjoy.

This paper assumes certain level of familiarity with the .NET Compact Framework and game development in general.

Full Screen Game Form

Often in gaming applications, it is desirable to use the full display real estate of the device. A Form that occupies the entire screen area is referred to as FullScreen Form (also known as Game Form). In other words, a FullScreen Form occupies the desktop (or client area) plus the non-client area such as the title / navigation bar at the top, borders and the MenuBar at the bottom.

An application creates a FullScreen form by setting its WindowState to Maximized as follows:

form.WindowState = FormWindowState.Maximized;

If there is a MenuBar (and / or a ToolBar on Pocket PC) attached to the Form, then it can't be made FullScreen.

On version 1.0 of the .NET Compact Framework on Pocket PC, to create a FullScreen application the WindowState property must be set inside the Form's OnLoad.

Figures 1 and 2 below illustrate the differences between FullScreen and Non-FullScreen Forms on a Pocket PC.

Figure 1. Non-Full Screen Form

Figure 2. Full Screen Form

The primary implication of having a FullScreen Form is that there is no title / navigation bar or Menubar. The application must take these factors into consideration and provide the means to get around these functionalities when necessary.

If you just want your Form to just fill the available desktop area (but not FullScreen) you need not do anything. By default, .NET Compact Framework will automatically size your Form to fill the screen on Pocket PC.

In fact, it is recommended that you don't explicitly set the ClientSize of your Form to accomplish this as that might hinder with the interoperability of your application between various Windows CE .NET devices. For example, if you explicitly size your application to match the form factor of one device it may not be desirable on a different device. It is advisable to go with the default size of the Form.

Overriding OnPaint and OnPaintBackground

A typical game application would custom draw the contents of the Form. It does so by overriding a control's OnPaint() event and custom handling the painting of the Form.

protected override void OnPaint(PaintEventArgs paintg)
{
   Graphics gx = paintg.Graphics;
   // Custom draw using the graphics object
}

Whenever a control begins to paint, its background is automatically refreshed first. For example while processing OnPaint() to paint the contents of the Control, its background is painted first with the color specified from this.Backcolor. This might be undesirable in the above case where the application is owner drawing the Form; the automatic painting of the background might result in momentary flashing of the background before the application had a chance to finish painting the foreground.

To prevent this default behavior, whenever an application overrides the OnPaint() method it is highly recommended that it also overrides the OnPaintBackground() method and paints the background itself. The application can choose to handle all the painting inside of OnPaint() and leave the OnPaintBackground() empty as in the example below.

protected override void OnPaintBackground(PaintEventArgs paintg)
{
   // Left empty, avoids undesirable flickering
}

Off Screen Bitmap Technique For Painting

You can draw on-screen by obtaining the graphics object for the screen via this.CreateGraphics() from a control and draw to it directly. You must always remember to dispose of the on-screen graphics object when it is no longer needed. Not doing so may cause starvation of limited display device's resources.

As seen in the previous section, you can access the screen's graphics object inside of the OnPaint() and OnPaintBackground() methods via PaintEventArgs.Graphics. These graphics objects are automatically disposed once the painting method is executed.

Often for gaming applications drawing directly on-screen is not ideal. Because when you are drawing many objects on-screen, you would start seeing the screen flash. To avoid this, game developers usually resort to off-screen drawing techniques.

The idea is to create an off-screen bitmap, obtain a graphics object for it, perform all the drawing operations against it (in memory) and copy the resulting off-screen bitmap to on-screen.

// Create off-screen graphics
Bitmap bmpOff = new Bitmap(this.ClientRectangle.Width,
  this.ClientRectangle.Height);
Graphics gxOff = Graphics.FromImage(bmpOff);

In this example, I am creating an off-screen bitmap exactly the size of the client area of the Game Form. Depending on the need, this can be varied. However, maintaining a 1-1 size relationship between off-screen and on-screen drawing bounds would be greatly beneficial especially when it comes to translating the coordinates of Sprites between on-screen and off-screen.

// Draw to off-screen graphics, using Graphics.Draw APIs
// Create on-screen graphics
Graphics gxOn = this.CreateGraphics();
// Copy off-screen image onto on-screen
gxOn.DrawImage(bmpOff, 0, 0, this.ClientRectangle, GraphicsUnit.Pixel);
// Destroy on-screen graphics
gxOn.Dispose();

This technique avoids flashing of the screen and is faster as all the off-screen drawing operations happen in memory.

Sprites

Raster images like Bitmaps are represented in rectangular format where as most real world sprites are of irregular shape (and not rectangular). So we need to find methods by which we can extract an irregular shaped Sprite image from a rectangular raster representation.

Color Key Transparency

One popular technique used by the game developers is Color Key technique, where the specified color key is left out from the bitmap when rendering. This technique is also referred to as chroma key masking, color killing and transparent blending.

Figure 3. Sprite.

Figure 4. With Color Key transparency

Figure 5. Without Color Key Transparency

In the Sprite bitmap (Figure 3), the non-object area is filled with magenta color, which is used as the color key. The resulting effect of blending with and without this technique is illustrated in figures 4 and 5 respectively.

The first step of transparent blending is to set up the color key (chroma key) that needs to be masked in the rendering. We need to specify an exact color key value; specifying a range is not supported.

ImageAttributes imgattr = new ImageAttributes();
imgattr.SetColorKey(Color.Magenta, Color.Magenta);

Instead of using the standard set of colors provided in the Color class, you can construct your own color directly by specifying the Red, Green and Blue (RGB) values as follows:

imgattr.SetColorKey(Color.FromArgb(255, 0, 255),
   Color.FromArgb(255, 0, 255));

Another technique that I often use to specify the color key is to use a pixel value directly. This eliminates the need to deal with RGB values. Further, color key is not hard coded and can independently be changed in the Bitmap.

imgattr.SetColorKey(bmpSprite.GetPixel(0,0), bmpSprite.GetPixel(0,0));

Now, let us see how we can use the color key that we have setup to transparently draw the Sprite.

gxOff.DrawImage(bmpSprite, new Rectangle(x, y, bmpSprite.Width,
   bmpSprite.Height), 0, 0, bmpSprite.Width, bmpSprite.Height,
   GraphicsUnit.Pixel, imgattr);

In the code snippet above, the destination rectangle is specified as new Rectangle(x, y, bmpSprite.Width, bmpSprite.Height) where x and y are the desired coordinates of the Sprite.

Often, it might be necessary to stretch or shrink the Sprite while drawing. You can do this by adjusting the width and height of the destination rectangle. In the same way, you can also adjust the source rectangle to draw only a portion of the Sprite, although this may not be very useful.

While setting up the bitmap for your Sprite, it is advisable to consider the color resolution of your target device. For instance, a 24-bit color bitmap may not render as expected on a 12-bit color resolution device; the difference in color gradient between the two might be very noticeable depending on the choices of color used. Also while selecting the ColorKey to be masked, make sure the color value is in the supported spectrum of the target display. Remember that only an exact ColorKey match is supported.

Color Key transparency is the only supported blending technique on the .NET Compact Framework.

Image as an Embedded Resource

You can embed an image resource into your assembly by adding the image to your project and setting its Build Action property to "Embedded Resource". See the online help topic "Embedding Resource Files in Applications" for more information.

We can then consume the embedded bmp from the resource as follows:

Assembly asm = Assembly.GetExecutingAssembly();
Bitmap bmpSprite = new Bitmap(asm.GetManifestResourceStream("Sprite"));

BMP, JPG, GIF and PNG image formats are supported by the Bitmap class.

Tuning up your Paint Method

Painting routines in a game need to be tightly optimized to get the best possible performance. Brute force method to painting the screen would be to erase and redraw all the sprites off-screen, then refresh on-screen with the resulting image from off-screen. This is inefficient, as we would be unnecessarily redrawing the entire screen every time. Here, our frame rate would depend on the rate at which the Compact Framework can refresh the whole screen.

A better approach would be to calculate the dirty area of the sprites in our game and refresh only the dirty portions of the screen. A sprite can become dirty for variety of reasons such as movement, change in image/color or collide with other sprites and so on. In this chapter, we will discuss various techniques that we can use to efficiently calculate the dirty areas.

Dirty Area Computation

Let us consider a moving Sprite, a simple way to compute the dirty area is to take the union of the old and new bounds.

RefreshScreen(Rectangle.Union(sprite.PreviousBounds, sprite.Bounds));

Where RefreshScreen() is a method that refreshes the specified rectangular region on-screen.

Figure 6. Dirty area as union of old and new bounds

Figure 7. Huge delta yields huge dirty area

Note   This technique can yield an unnecessarily bigger dirty rectangle, as illustrated in Figure 7, if the delta between the old and new coordinates is high and / or the size of the sprite is large (for illustration purposes the old boundary is shown in different color).

In this case, it would be efficient to compute the dirty area of the Sprite as several unit rectangles that when put together represent the dirty area.

First let us find out whether the old and new bounds are overlapping or not. If not, then we can simply calculate the dirty area as two individual rectangles representing the old and the new bounds respectively. So, for the scenario illustrated in Figure 7 it would be wise to treat the old bounds and the new bounds as two separate dirty areas.

if (Rectangle.Intersection(sprite.PreviousBounds,
sprite.Bounds).IsEmpty)
   {
      // Dirty rectangle representing old bounds
      RefreshScreen(sprite.PreviousBounds);

      // Dirty rectangle representing current bounds
      RefreshScreen(sprite.Bounds);
   }

The above technique would also work in places where we don't mind redrawing the overlapped area twice as is demonstrated in Figure 8 below.

Figure 8. Dirty area split into old and new bounds

Now, let us look at how we can calculate the dirty area as several unit rectangles that together represent the partially overlapping old and new boundaries such that no dirty area is repeated twice, meaning all the unit rectangles are mutually exclusive.

First, include the new bounds as a whole unit. Note that this includes the overlapping area between the old and new boundaries.

Unit Dirty Area 1: Dirty Rectangle Representing Current Bounds

RefreshScreen(sprite.Bounds);

Figure 9. Dirty area split into several units

Now, split the non-overlapping part of the old bounds into two individual units as seen in the code below:

Rectangle rcIx, rcNew;
// Calculate the overlapping intersection
rcIx = Rectangle.Intersection(sprite.PreviousBounds, sprite.Bounds);

Unit Dirty Area 2

rcNew = new Rectangle();
if (sprite.PreviousBounds.X < rcIx.X)
{
   rcNew.X = sprite.PreviousBounds.X;
   rcNew.Width = rcIx.X - sprite.PreviousBounds.X;
   rcNew.Y = rcIx.Y;
   rcNew.Height = rcIx.Height;
}
else
{
   // Means sprite.PreviousBounds.X should equal to rcIx.X
   rcNew.X = rcIx.X + rcIx.Width;
   rcNew.Width = (sprite.PreviousBounds.X + 
sprite.PreviousBounds.Width) - (rcIx.X + rcIx.Width);
   rcNew.Y = rcIx.Y;
   rcNew.Height = rcIx.Height;
}

RefreshScreen(rcNew);

Unit Dirty Area 3

rcNew = new Rectangle();

if (sprite.PreviousBounds.Y < rcIx.Y)
{
   rcNew.Y = sprite.PreviousBounds.Y;
   rcNew.Height = rcIx.Y - sprite.PreviousBounds.Y;
   rcNew.X = sprite.PreviousBounds.X;
   rcNew.Width = sprite.PreviousBounds.Width;
}
else
{
   rcNew.Y = rcIx.Y + rcIx.Height;
   rcNew.Height = (sprite.PreviousBounds.Y +
sprite.PreviousBounds.Height) - (rcIx.Y + rcIx.Height);
   rcNew.X = sprite.PreviousBounds.X;
   rcNew.Width = sprite.PreviousBounds.Width;
}

RefreshScreen(rcNew);

Collision Detection

Now, let us look at the case where a sprite collides with another sprite. From the drawing stand point, you can simply ignore the collision between the two sprites and just update the dirty portions of the sprites individually using the techniques discussed earlier.

But often you would want to detect the collision of sprites for game response purpose. For example in a shooting game when a bullet hits the target, you would want to visually react to that with an explosion or such.

I will not discuss in detail the different collision detection techniques that are available in this article. However, I will highlight few of them.

Let us recall that most sprites are of irregular shape, but the raster images that represent them are rectangular. It is difficult to represent the bounds (or envelope) of a sprite as a free region so we resort to representing it through the enclosing rectangle.

When the player perceives the Sprite on the screen, he/she is actually looking at the sprite area and is oblivious to the non-sprite area. Hence, any collision detection between sprites must only happen between their respective sprite areas and should not include non-sprite area.

In the case of smaller sprites, it is generally possible to use the entire sprite bounds when calculating the collision and get away with it visually. Since the objects are small and moving fast, the human eye will not notice the delusion. A simple rectangle intersection of the bounds of the two sprites would suffice.

Rectangle.Intersect(sprite1.Bounds, sprite2.Bounds);

If the sprites are circular in shape, then we can simply compute the distance between their centers and subtract their radii; if the result is greater than zero then we have detected a collision.

For pixel-by-pixel hit detection, you can use the Rectangle.Contains method:

if (sprite.Bounds.Contains(x,y)
   DoHit();

Quick boundary intersection technique should be applied first to detect boundary collision between Sprites. If a collision occurred, then we can use a more precise method such as a collision bitmap mask technique to identify the overlapping pixels.

If we don't care for pixel-level granularity then we can use the techniques that we have already seen in the dirty area computations section to compute the collision area. Instead of dealing with old and new bounds of a sprite we would be dealing with the bounds of two colliding sprites.

Collision detection techniques are specialized and should be determined on a case-by-case basis. Choosing a particular technique depends on variety of factors such as size and shape of the Sprite, the nature of the game and so on. It is not uncommon to use several of these techniques in a single game.

Sprite Velocity

Often frame rate is misconstrued for the velocity of a moving sprite. We should not just rely on the frame rate but also regulate the distance the sprite moves per frame to get the desired net velocity.

Let us consider the following example where we want our sprite to move 100 pixel units vertically in a second. Now we can fix the frame rate at 10 fps and move the sprite 10 pixels vertically per frame to achieve the net velocity or we can increase the frame rate to 20 fps and drop the sprite's vertical delta per frame to 5 pixels.

Either way we achieve the same net velocity, the difference is that in the former case the movement of the sprite might be bit jumpy visually as it moves the distance in fewer refresh cycles than the later. But in the later case we are relying on our game to render at 20 fps, we need to be absolutely certain of the capabilities of the hardware, system and the .NET Compact Framework before deciding on either these approaches.

Game Progression Techniques

A game is said to be progressive, when the screen changes over time and visually responds to user interactions.

Game Loop

Inside the game, we initiate, maintain and tear down a loop, which would give us the opportunity to render the screen when necessary. Typically, the game loop is initiated when the game starts and maintained there after by sleeping and looping back as needed until the game ends—at which time the game loop is popped.

This technique offers the maximum flexibility and quick turn around time needed by action games. We will now implement a game loop for our football game. Let us assume that the game has multiple levels.

private void DoGameLoop()
{
   // Create and hold onto on-screen graphics object
// for the life time of the loop

   m_gxOn = this.CreateGraphics();

   do
   {
      // Init game parameters such as level

      DoLevel();
      
      // Update game parameters such as level   

      // Ready the game for the next level
   }
   while (alive);

   // End game loop

   // Dispose the on-screen graphics as we don't need it anymore

m_gxOn.Dispose();

   // Ready the game for next time around
}

private void DoLevel()
{
   int tickLast = Environment.TickCount;
   int fps = 8;

   while ((alive) && (levelNotCompleted))
   {
      // Flush out any unprocessed events from the queue 

      Application.DoEvents();

      // Process game parameters and render game

      // Regulate the rate of rendering (fps) 
// by sleeping appropriately
      Thread.Sleep(Math.Abs(tickLast + (1000/fps) –
Environment.TickCount));
      tickLast = Environment.TickCount;
   }
}

Note that we are calling Application.DoEvents() inside the loop every time before looping back, we need to do this to maintain active communication with the system and to yield for processing of messages that are pending in the events queue. This is necessary because when our app is in a loop we essentially lose the ability to process any incoming message from the system; unless we call Application.DoEvents() explicitly our app will not respond to system events and consequently will have undesirable side effects.

Another important factor to consider in a game loop is the rate of rendering. Most gaming animation needs at least 8-10 frames per second (fps). To give you an idea, a typical cartoon film is rendered at 14-30 frames per second.

A simple frame rate regulation technique is to decide on the fps needed and sleep (1000/fps) millisecond inside the loop. But we also need to also take the time it takes to process the current tick into account. Otherwise our rendering rate will be lower than expected. The processing time might be considerable especially on slower hardware as it involves high cost operations such as handling user input, rendering the game and so on.

So we want to sleep 1000/fps less the time we take to process current tick in milliseconds before proceeding with the loop.

Thread.Sleep(Math.Abs(tickLast + (1000 / fps) - Environment.TickCount));
tickLast = Environment.TickCount;

Timer Callback

Another technique is to set up a system timer that calls back periodically. As with the game loop, typically the timer is instantiated when the game starts and the timer tick events (which happen at regular intervals) are processed until the game ends, at which time the timer is disposed.

This is simpler compared to the game loop as we are letting the system handle the timer loop for us. We just need to process the timer callback and progress our game by drawing one frame at a time. Further, we don't have to worry about explicitly draining the events queue.

But we have to be very careful in choosing the tick interval of the timer as it decides the frame rate of our game.

In the game loop technique time interval between two ticks is completely in our control and as seen earlier this can be easily regulated to take the processing time into consideration. On the other hand, timer call back means the interval can't be varied. So we need to either choose the tick interval large enough to complete processing each call back or regulate the processing of ticks by explicitly keeping track of the time it takes to process a tick and skip tick(s) as necessary to maintain the rhythm.

One major drawback with timer callback is that we are relying on the resolution of the Operating System timer. The lowest tick interval possible is decided by the highest possible resolution of the timer. This can be a limiting factor on Pocket PC where the timer resolution is low which implies the possible fps in this approach is low as well. Also, the priority of the Operating System timer events is pretty low which means the responsiveness of the game will be low as well.

In spite of the limitations, this technique is well suited for slow progressing games where frame rate is not all that important. For example, screen savers

private void StartTimer ()
{
   int fps = 8;

   // Create and hold onto on-screen graphics object 
// for the life of the game/timer
   m_gxOn = this.CreateGraphics();
   
   // Setup timer callback to happen every (1000/fps) milliseconds
m_tmr = new System.Windows.Forms.Timer();
   m_tmr.Interval = 1000/fps;

   // Specify the timer callback method
   m_tmr.Tick += new EventHandler(this.OnTimerTick);

   // Start the timer
   m_tmr.Enabled = true;

   // Init game params such as level
}

protected void OnTick(object sender, EventArgs e)
{
   if (alive)
   {
      // Regulate tick to include processing time, 
// skip tick(s) if necessary

      if (processTick)
      {
         // Process game parameters and render game
         if (levelCompleted)
         {
            // Update game params such as level
            }
      }
   }
   else
      EndTimer ();
}

private void EndTimer ()
{
   // End game

   // Dispose timer
   m_tmr.Dispose();

   // Dispose the on-screen graphics as we don't need it anymore

m_gxOn.Dispose();
   m_gxOn= null;   // Make sure the garbage collector gets it

   // Ready the game for next time around
}

Note: You must dispose the timer when the game ends.

Observe that there is no need for Application.DoEvents() as we are neither looping nor blocking any system events, in fact timer callback is just another system event. Also, note that most of the game logic is pushed inside the OnTimerTick() event handler.

Invalidate-Update

Another way to progress the game is to render it on an adhoc basis. Whenever the game logic detects that the screen needs to be refreshed, we can request the system to invalidate and refresh the appropriate portion of the screen.

This technique is the simplest of all and best suited for games whose progression is based on user interaction. That is those games that does not constantly tick and render (as seen in game loop, timer callback) but advances only when the user interacts with it, for example puzzle games.

We can invalidate the entire client area of our Game Form by calling this.Invalidate() or invalidate only a potion of it by calling this.Invalidate(dirtyRect).

Just calling this.Invalidate() alone doesn't guarantee that painting will happen in a timely fashion. We must follow through by calling this.Update() to ensure that the screen is refreshed before proceeding further. Asynchronously calling Invalidate() and Update() might be helpful in getting higher performance in some cases. But without proper frame synchronization techniques, it might result in artifacts appearing on the screen and / or frames getting dropped.

If we can afford to refresh the whole screen every time, then we can simply call this.Refresh(), which ensures that Invalidate() and Update() happen in sequence.

When we are owner drawing the Form, we can call this.Refresh() when any part of the screen needs to be refreshed and internally keep track of the dirty portions of the screen and selectively refresh the screen inside of OnPaint() and OnPaintBackground().

Optimizing Startup Time

Often in games it is not uncommon for the developer to initialize all the game parameters upfront and avoid unnecessary delay at runtime while the game is playing. The down side of this approach is that the delay is shifted to the startup of the game and might be unpleasant especially if the game is taking too long to load before the user gets a chance to interact with it.

In this case, it is desirable to show a splash screen as fast as we can with game-related information and engage the user. We can then do the startup activities such as loading resources, initializing game parameters and so on in the background.

We can either use a separate FullScreen Form as a splash screen or just use the main Game Form itself with bare bone game information.

public Game()
{
   // Set visibility first   
   this.Visible = true;

   // Create on-screen graphics
   Graphics gxOn = this.CreateGraphics();

   // Display Splash screen
   DoSplashScreen(gxOn);

   // Destroy on-screen graphics   
   gxOn.Dispose();

   // Proceed with your Game Screen
}

void DoSplashScreen(Graphics gxPhys)
{
   // Load minimal resources such as title bitmap 

   Assembly asm = Assembly.GetExecutingAssembly();
   Bitmap bmpTitle = 
new Bitmap(asm.GetManifestResourceStream("title"));

   // Draw the title screen – this is your splash screen
   gxPhys.DrawImage(bmpTitle, 0, 0);

   // Now proceed with loading rest of the resources 
// and initializing the game

   // Regulate the splash time if necessary
}

It is important not to provide any functionality in the startup splash screen, rather it should be used only as an introduction / information page. It is not always necessary to start with a splash screen.

Game Buttons

Navigation Keys

On Pocket PC the navigation keys (namely the left, right, up and down keys) play a vital role in games. We can get access to the KeyDown, KeyPress and KeyUp events for these keys by overriding the respective event methods of our game Form.

Usually, we want to process the KeyDown event for these navigation keys and provide game level functionalities.

protected override void OnKeyDown(KeyEventArgs keyg)
{
   switch(keyg.KeyData)
   {
   case Keys.Left:
      // Provide game functionality for Left key
      break;

   case Keys.Right:
      // Provide game functionality for Right key
      break;

   case Keys.Up:
      // Provide game functionality for Up key
      break;

   case Keys.Down:
      // Provide game functionality for Down key
      break;

   default:
      // We don't care
      break;
   }

   // Always call the base implementation 
// so that the registered delegates for this event are raised.
   base.OnKeyDown(keyg);
}

Pocket PC Stylus

Stylus on Pocket PC acts like mouse on desktop. We can get access to MouseDown, MouseMove and MouseUp events by overriding the respective event methods of our Game Form.

protected override void OnMouseDown(MouseEventArgs mouseg)
{ 
   Point ptHit = new Point(mouseg.X, mouseg.Y));

}

On Pocket PC, as of V1.0 the .NET Compact Framework does not support right mouse button and hardware buttons.

Other Tidbits

Draw the image when possible instead of using a bitmap. This will reduce the size of the memory and can increase performance too. For example, in a space shooting game rather than using a scrolling bitmap as background, you can get the same effect by filling the background with black rectangle and drawing the stars.

Try logically grouping similar bitmaps into one big bitmap and later use the relevant coordinates to extract the appropriate unit bitmap as needed. Having one large bitmap instead of several smaller ones will reduce the resource size.

Try using image formats other than BMP where possible to take advantage of better image compression (for example JPEG).

Avoid having controls on an owner drawn Game Form. You should owner draw everything. For example, if you want a Label, use Graphics.DrawString() instead or create custom Sprites.

Initialize as much of your game logic as possible statically so that you can avoid costly computations at run time. For example, in a puzzle game you can statically store the winning combinations if possible upfront rather that using costly dynamic algorithms and such while the game is running.

Conclusion

When writing games for devices such as Pocket PC, remember that the display screen size is much smaller and the hardware is less powerful than desktop PC.

So tightly optimize your game even more so for the tiny devices than you would for the desktop. Also carefully consider the capabilities of the target hardware, operating system and the .NET Compact Framework when designing the game.

A game's performance battle is fought and won by its paint routines. Efficient painting techniques decide the response of the game, especially in small devices such as Pocket PC. So use all the painting tune-ups (such as dirty-area computation) as seen earlier to save on per-frame painting time.

Frame rate is another important factor to keep in mind, choose this wisely based on the capabilities of the target devices.

More Information

For more information, please see the resources below:

Show:
© 2015 Microsoft