Export (0) Print
Expand All

Games Programming with Cheese: Part Four

.NET Compact Framework 1.0
 

Rob Miles
Department of Computer Science, University of Hull

November 2004

Applies to:
   Windows Mobile™-based Smartphones
   Windows Mobile 2003 Second Edition software for Smartphones
   Microsoft® .NET Compact Framework version 1.0
   Microsoft Visual Studio® 2003

Summary: An introduction to games programming on Windows Mobile-based Smartphones using the .NET Compact Framework and Visual Studio 2003. There are a number of downloads of the project at different stages of development. (17 printed pages)


Download Games Programming with Cheese 4.msi from the Microsoft Download Center.

Contents

Introduction
Toward a Well-Behaved Application
Implementing a Resume Key
Saving Game State
Smartphone Program Management
Application Icons
Deploying the Program
Conclusion

Introduction

In the previous articles in this series, two games were developed. This article will show how the first game can be made ready to deploy on an actual Smartphone device. This means that it must be made into a well-behaved application that does not steal processor power and battery life when it is not being used. It must also be capable of saving its own state so that games can be paused and resumed when required. Finally, it must have an icon that the user can select.

Toward a Well-Behaved Application

At present, the programs that have been created work but they are not very well-behaved programs. All the time that the game is in memory, it is consuming processor power with each timer tick. Even if the screen is not displayed, the program behind it is still updating in the background. Also, if the player switches to another application (or a phone call comes in), the screen disappears and the player loses control of the game. This could be very frustrating if the player is approaching a high score when a call comes in.

Handling Loss of Focus

In the Microsoft® Windows® Forms environment, the window that has focus is the one that is responding to keyboard and mouse events. A user normally determines focus by using the mouse to select a particular window or by using shortcut keys such as ALT+TAB. A Windows desktop may display a large number of application windows at the same time, and the window with focus is always denoted by the bolder color of the title bar.

In the case of the Smartphone, only one application can be active on the screen at a given time. However, the concept of focus is still important because it is how an application can be informed that it is no longer the one that is visible to the user.

In Windows, the LostFocus event is raised when focus is lost. On a Windows desktop application, this would be raised when the user selects a different application window. On a Smartphone, the user can press the Back or Home keys to switch from one application to another. As shown in Figure 1, the LostFocus event is managed in the same way as other form events.

Figure 1. The LostFocus event

In the property screen shown in Figure 1, the method Form1_LostFocus has been attached to the lost focus event. This means that when the game application loses control of the screen (that is, something is displayed in its place), this method is called.

The game application can attach a method to this event that puts the game into sleep mode when it is not visible. In sleep mode, the application is still in memory and all the game values are being maintained. However, the screen does not update and the game does not progress.

With the games that have been created so far, the best way to put the game to sleep is to stop the timer on the form. It is the timer event that causes the game to be updated and the screen to be redrawn. Stopping the timer will effectively stop the game. A suitable sleep method to do this would be as follows:

private void sleep () 
{
    timer1.Enabled = false;
}

This method would be called from the lost focus event so that when the game is not visible on the screen, the update stops:

private void Form1_LostFocus(object sender, System.EventArgs e)
{
    sleep();
}

Of course, if the application just left it at this, the user would be very unhappy. The first time the game lost focus it would be stopped forever. The program must contain a complementary method that resumes the game when the GotFocus event is raised. The GotFocus event is raised on a form when the user returns that display to the screen.

The wake method must be a direct inverse of the sleep method. The application can attach an event handler to the GotFocus event, which contains a call of wake:

private void wake () 
{
    timer1.Enabled = true;
}

private void Form1_GotFocus(object sender, System.EventArgs e)
{
    wake();
}

From a design point of view, this is the correct way to do it. The event hander code should not be placed directly into the handler itself. It may be that later the program could contain a sleep command that allows the user to pause the game from a menu selection. In this case, it is much easier to achieve that effect if the program can just call a method to do the job.

With these methods in place, the game will stop when the form is hidden and start when it is displayed. If you run the program in the supplied project, you will find that if you press the Home key on the phone while the application is running, the display will switch to the home screen. If you then use the Back key, you will be taken back to the game at exactly the point where you left it.

Implementing a Resume Key

The only problem with this approach is that the user is taken straight back into the game when it resumes. It would be more sensible to provide a way that allows the user to return to the game in a more relaxed state. Otherwise, the user may not be ready for the game to restart and might lose a life before getting back in control of the game.

Restart can be made more convenient by renaming the left soft key when a game is interrupted. Then, when the game is resumed, the player must press the left soft key to restart the timer and resume the game. However, this behavior is undesirable when the game is displaying the "attract" mode, because the user will not like having to press Resume followed by Start to begin a new game. Remember that the game is in attract mode when it is not being played and it is trying to "attract" the user to play it by showing a demo of the game. Resuming the game can be implemented in the following way:

private void sleep () 
{
    if ( gameLive ) 
 {
 menuItem1.Text = "Resume";
 }
 timer1.Enabled = false;
}

The gameLive flag is true when a game is being played. If the sleep method is called during an active game, the text of the left soft key is changed to Resume just before the timer is stopped, as shown in Figure 2.

Click here for larger image

Figure 2. Resuming a game. Click the thumbnail for a larger image.

Now the program must also be changed so that the timer is not restarted when the application is given focus. Instead, the timer must restart when the Resume soft key is pressed. To achieve this behavior, the action of the menu item must be modified as follows:

private void menuItem1_Click(object sender, System.EventArgs e)
{
    if ( menuItem1.Text == "Resume" ) {
 timer1.Enabled = true;
 menuItem1.Text = "Start";
 }
    else 
 {
 startGame();
 }
}

The method uses the text on the button as a way of remembering state. If the button has been pressed in the Resume context, it will put the text back to the Start action and restart the timer. Otherwise, the button is being pressed to start a new game.

The final item of code to finish off is the wake method. This must resume the timer only if the game is in "attract" mode — that is:

private void wake ()
{
    if ( !gameLive ) 
 {
 timer1.Enabled = true;
 }
}

If you run the program in the supplied sample project and if you switch focus from the game, you will see that the left soft key is now labelled Resume when you return to the game. Pressing the Resume key causes the uncompleted game to continue.

Saving Game State

The pattern of game play on a Smartphone will be quite different from other game platforms. The player will probably want to play in very short bursts. This means that after each burst of play, the game must retain all its state exactly so that it can be resumed at any time. All this must happen automatically. Therefore, the game application must contain methods that save and load game state at appropriate times.

Simple Save and Restore

The simplest way to preserve the game state is to work through the program and note all the object properties that need to be saved. Each of these properties can then be saved in a file. In the case of the cheese game, these properties include items like the position of the cheese on the screen and the current direction of movement. A method to save them simply writes each into a file:

private string stateFileName = "Games1.bin";
private bool saveState () 
{
    System.IO.Stream stream = null;
    System.IO.BinaryWriter writer = null;
    try 
    {
        stream = 
            System.IO.File.Open(applicationDirectory+stateFileName,
            System.IO.FileMode.OpenOrCreate);
        writer = new System.IO.BinaryWriter(stream);
        writer.Write(highScorePlayer);
        writer.Write(highScoreValue);
        writer.Write(gameLive);
        if ( gameLive ) 
        {
            writer.Write(Cheese.X);
            writer.Write(Cheese.Y);
            writer.Write(Bread.X);
            writer.Write(Bread.Y);
            writer.Write(xSpeed);
            writer.Write(ySpeed);
            writer.Write(goingDown);
            writer.Write(goingRight);
            writer.Write(Ham.Visible);
            writer.Write(Ham.X);
            writer.Write(Ham.Y);
            writer.Write(hamTimerCount);
            foreach ( BackSprite s in tomatoes ) 
            {
                writer.Write(s.X);
                writer.Write(s.Y);
                writer.Write((int)s.DrawState);
            }
            writer.Write(tomatoDrawHeight);
            writer.Write(livesLeft);
            writer.Write(scoreValue);
            writer.Write(Field.Message);
        }
    }
    catch 
    {
        return false;
    }
    finally 
    {
        try 
        {
            writer.Close();
            stream.Close();
        }
        catch {}
    }
    return true;
}

The saveState method in the preceding code stores the state of all the game objects in a file. When the exit menu item is selected or when the game is requested to shut down by the operating system, this method is called. It opens a binary stream and then writes all the information of interest into it. The full game information is output only if a game is actually in progress; otherwise, just the high score information and the game live flag is written. The method returns true if it worked, and false if it did not.

The load method is a reverse of the save method:

private bool loadState () 
{
    if ( !System.IO.File.Exists(applicationDirectory+stateFileName) )
    {
        return false;
    }
    System.IO.Stream stream = null;
    System.IO.BinaryReader reader = null;
    try 
    {
        stream = System.IO.File.OpenRead(
            applicationDirectory+stateFileName);
        reader = new System.IO.BinaryReader(stream);
        highScorePlayer = reader.ReadString();
        highScoreValue = reader.ReadInt32();
        gameLive = reader.ReadBoolean();
        if ( gameLive ) 
        {
            Cheese.X = reader.ReadInt32();
            Cheese.Y= reader.ReadInt32();
            Cheese.Visible = true;
            Bread.X= reader.ReadInt32();
            Bread.Y= reader.ReadInt32();
            Bread.Visible = true;
            xSpeed= reader.ReadInt32();
            ySpeed= reader.ReadInt32();
            goingDown= reader.ReadBoolean();
            goingRight= reader.ReadBoolean();
            Ham.Visible= reader.ReadBoolean();
            Ham.X= reader.ReadInt32();
            Ham.Y= reader.ReadInt32();
            hamTimerCount= reader.ReadInt32();
            foreach ( BackSprite s in tomatoes ) 
            {
                s.X= reader.ReadInt32();
                s.Y= reader.ReadInt32();
                s.DrawState= (BackSprite.BackSpriteState) reader.ReadInt32();
                if ( s.DrawState == BackSprite.BackSpriteState.visible) 
                {
                    s.DrawState = BackSprite.BackSpriteState.appearing;
                }
            }
            tomatoDrawHeight= reader.ReadInt32();
            livesLeft= reader.ReadInt32();
            scoreValue= reader.ReadInt32();
            Field.Message= reader.ReadString();
        }
        reader.Close();
        stream.Close();
    }
    catch  
    {
        return false;
    }
    finally 
    {
        try 
        {
            if (reader != null) reader.Close();
        }
        catch {}
        try 
        {
            if (stream != null) stream.Close();
        }
        catch {}
    }
    return true;
}

To make sure that it never hoards resources, the load method uses the same techniques as the save method. The load method also ensures that the file exists before it tries to read it. If the file is not found or the read process fails, the method returns false. Note that when reading items back, the read method appropriate to the type of data saved must be used. Otherwise, the method may complete but the data returned will be meaningless.

The whole of the save information is fetched only if a game is active. Also, by setting the tomatoes' value to BackSprite.BackSpriteState.appearing, the loadState method causes the tomatoes to appear when the screen is next redrawn. The method is called just after the game has loaded so that the game is resumed automatically:

if ( !loadState() ) 
{
    gameLive = false ;
}
if ( gameLive ) 
{
    // Sleep if the game is active--user can then start by resuming.
    sleep();
}
else 
{
    // Display attract mode.
    // Start the clock.
    timer1.Enabled = true;
}

If the load fails, the game is put into the attract mode. If the load is successful and the game is running, it is put to sleep so that the player gets the option to resume it rather than have it start up instantly.

This load method has some error handling, but it is not exhaustive. If the load fails partway through reading a file, some of the game objects will be in the wrong position. However, because the game cannot resume because of the error, the player is unaffected. When a new game is started, everything is restored to its original position.

The save method call is placed in the event hander for the exit menu item:

private void menuItem2_Click(object sender, System.EventArgs e)
{
    saveState();
    Application.Exit();
}

The sample project implements the save and restore so that a game is automatically saved during play.

Smartphone Program Management

The user of a Smartphone can select the active program from a display of program icons. Precisely how these icons are displayed varies, depending on the "skin" that has been applied to the display. The default display is shown in Figure 3.

Click here for larger image

Figure 3. Selecting an application. Click the thumbnail for a larger image.

The application highlighted (and outlined in red in Figure 3) will be activated when the user presses the select button on the phone keypad. In the illustrated example, this would select the Microsoft Messenger program. For a game program to be a valid application, it must have an icon so that it can be selected in this way.

Application Icons

The icon for an application is held within the application file. It is a small bitmap. A program file may contain several different icon bitmap sizes depending on the needs of the operating system. For a Smartphone application, the programmer must provide two icon bitmaps, one 16 x 16 pixels in size and the other 32 x 32 pixels in size. These icon designs will be stored in an icon file that is part of the application project. When the program is built, the images in this file will be added to the application file for the Smartphone operating system to use.

The first thing that must be done to give a program an icon is the creation of the icon file. This is created in Microsoft Visual Studio by using the following sequence:

  1. Select Add New Item from the Project menu (or use the keyboard shortcut CTRL+SHIFT+A).
  2. In the dialog that appears, select the Icon File item from the templates, as shown in Figure 4.

    Figure 4. The Icon File template

  3. The filename can be left at the default of Icon1.ico.
  4. Click Open to create the resource, add it to the project, and then open it for editing.

Visual Studio contains an icon editing program. The icons that will be edited take the form of 16 x 16 and 32 x 32 pixel versions. The default icons use 16 colors, and the editor appears as shown in Figure 5.

Figure 5. Default icon

The blue/green parts of the image are important; they are transparent and will let the background show through. You should select this color for the edges of your icon image.

Changing to 256-Color Icons

The icon images can be created in 16 colors, but they look much better with a wider color range. You might want to replace the 16-color default icons with 256-color ones. This will make the program slightly larger, but this is not a problem. To change the icon image formats, you proceed as follows:

  1. While editing an icon image, select the Image command from the top menu.
  2. From the menu that appears, select the New Image Type item, as shown in Figure 6.

    Figure 6. Adding a new Image Type

  3. From the dialog box that appears, you can select the new image type, as shown in Figure 7.

    Figure 7. Selecting the Icon Image format

  4. The two image types that are required are 16 x 16 in 256 colors and 32 x 32 in 256 colors. You should add one each one in turn.
  5. Now you must remove the 16-color images in the icon. To do this, you select each of them in turn and delete them. To select an icon image format, open up the Image menu and open up the Current Icon Image Types, as shown in Figure 8.

    Figure 8. Selecting an Icon Image Type

  6. Select one of the 16-color image types from the list provided.
  7. Open the Image menu again, and select Delete Image Type from the list.
  8. Repeat steps 5-7 for the other 16-color images.

Now the icon images must be created. The actual images to be used as icons can be pasted onto the icon in the same way that images are moved between other drawing applications. Simply select the image to be used, and then, using the icon editor in Visual Studio, paste the image onto the bitmap. The editor will make its best effort to map the colors in the source graphic onto the 256 available. It can also be used to scale the image to fit on the icon. The cheese image, shown in Figure 9, makes a reasonable icon.

Figure 9. The cheese icon

The background has been set to transparent. The cheese icon now needs to be assigned to the game program. This is done from the project properties dialog, which is available from the Project menu, as shown in Figure 10.

Click here for larger image

Figure 10. Setting the application icon. Click the thumbnail for a larger image.

Now, when the program is built, it will have the cheese image as the icon for the executable file. For your convenience, the sample project has a cheese icon set for the application.

Deploying the Program

Up until now, the game applications have been executed from within Visual Studio. For the game to be truly usable, it must be deployed inside the Smartphone itself. There are a number of ways that the program can be made available to a user in this way.

Copying the Program File

The simplest way to deploy the program is to copy the program file into the Smartphone storage. If the program file is placed in the right location in the file store, the user can navigate to the file and run it from the start menu. Files can be copied using Microsoft ActiveSync. By selecting the Explore icon in ActiveSync, the entire file store can be navigated.

Note   It is important to be careful when changing files in the phone because mistakes may cause the phone to work incorrectly.

The best place to put the cheese programs is in the Games folder in the start menu. You can do this by navigating to the appropriate folder, as shown in Figure 11.

Click here for larger image

Figure 11. The Bouncer Program in the Smartphone Games directory. Click the thumbnail for a larger image.

The program executables created by Visual Studio are found in the Bin directory of the project area on the host PC. There are two directories containing executables. They are called Debug and Release. The actual program binary in each is the same, but when the Debug version is built, the debug database is built as well. Just copying the executable into the Smartphone will allow the program to be accessed and used on the phone.

For example, the file Bouncer.exe has been copied into the Games folder on the Start menu for the phone, using ActiveSync. This file can then be selected by the user.

Note   The program icons are not displayed in the directory in the explorer section of ActiveSync. These icons are visible only on the device itself.

When you copy the file into the phone, it can take several minutes for the menus to be rebuilt so that the program is visible. You can force them to refresh by pressing the Home button, then select the left soft key (Start) and navigate to a folder (for example Accessories). If you now press the Back key the Start menu will be refreshed.

We now have a couple of playable games that can be deployed to Smartphone devices and fill in a few minutes of time.

Conclusion

In the four articles of Games Programming with Cheese, we have seen how easy it is to create an application — in this case a game — for the Windows Mobile-based Smartphone using the Microsoft .NET Compact Framework. We have seen how to draw graphics on the screen, how to develop simple gameplay, and how to add sound to enrich the gaming experience. In this instalment, we have achieved the most important aspect of programming for phone platforms, making the game phone user friendly by ensuring it handles incoming calls and short play cycles.

The skills we have learned in developing our games apply to the Pocket PC platform as well as the Smartphone platform. What we have learned in this article applies to writing games as well as line of business applications. Good user experience and non-intrusive integration to the phone are key to a successful application.

Happy gaming.

Show:
© 2014 Microsoft