Chapter 24. Keeping Track of High Scores

Unless you grew up during the heyday of arcade games in the 1980s, you might not have an appreciation for the sense of nerd accomplishment associated with a top spot on a game’s high score list. The high score list in arcade games serves as a public acknowledgement of who has the time, skills, and quarters to be the best of the best. If you think I’m dramatizing this a bit, keep in mind that a major plot device within a Seinfeld episode involved the character George Castanza attempting to move a Frogger game across a busy street while connecting it to a temporary battery supply to keep his high score from being lost. Even if you don’t have a large nerd ego, it can be rewarding to know that you placed within the upper ranks of those who have played a game before you. This hour shows you how to develop a high score list for the Space Out game that is saved to disk so that the scores are retained between games.

In this hour, you’ll learn:

  • Why it’s important to keep track of high scores

  • How to represent high score data in a game

  • How to store and retrieve high score data using a file

  • How to add a high score list to the Space Out game

The Significance of Keeping Score

When you think of video games, the word “achievement” might not be the first thing that comes to mind. You might not approach video games with the idea that you could be the best player of a particular game, but people are out there who do. In fact, the concept of a professional “cyber athlete” is already alive and real—companies are paying the best of the best video game players to test and promote their games. If you aspire to such a unique career, I have to warn you that thousands of hours of game play await you.

I brought up the issue of “cyber athletes” because it has a lot to do with the topic of this hour, high scores. Nowadays, the best video game players are determined in national tournaments where people get together and compete head-to-head. However, in years past, the best players were known only by their three initials that appeared in the high score lists of arcade games. The high score list in a classic arcade game was quite important to many early gamers because it was their only way to show off their gaming achievements.

It’s kind of sad really that high score lists aren’t as popular as they once were, but we can’t lament advances in technology. On the other hand, it doesn’t mean that high scores are entirely a thing of the past. For example, many popular games, such as Tony Hawk Pro Skater, still rely on a score to indicate how well a player performed. So, the idea of using a numeric score to measure your video game playing prowess is still valid. What has changed is that the shift away from arcade games has made it less of an issue to keep track of high scores. However, I still like the idea of a high score list—even if it’s only shared between friends.

This hour focuses on adding a high score list to the Space Out game that you’ve worked on in previous hours. A high score list presents a new challenge to you as a game programmer because you must store away the scores so that they can be retrieved even when a game program is closed. This requires storing the scores to disk and then reading them back later, which is a unique discipline in game programming. The first step is to figure out how to model the high score data, which means that you need to determine what you’re going to keep up with, and how.

Modeling High Score Data

I would love to tell you that I’m going to show you how to create a classic arcade game high score feature in which you get to enter your name or initials, and then see them displayed for all to see. Unfortunately, the seemingly simple task of allowing a player to enter his name or initials is fairly complex from a game programming perspective. Or more accurately, it requires a significant enough sidestep from the topic at hand that I don’t want to burden you with the details. So, you’re instead going to focus on a high score list that simply keeps up with the top five scores for a game, without any personalization associated with the scores. Although this approach to keeping track of a high score list doesn’t give credit for each score, it’s still a useful means of keeping up with the top five scores for a game.

Because you aren’t going to worry with storing the name of the person for each score, you only have to contend with storing away five numbers. At this point, you have to consider how many digits you need to represent the maximum possible score for a game. In the case of the Space Out game, it’s virtually impossible to score beyond four digits, which means that you can safely make a five-digit score the maximum. This also means that each score in the high score list is capable of having five digits. Of course, the number of digits in an integer in the game isn’t really a critical factor; the number of digits enters the picture when it comes time to store the scores to disk, and then read them back.

The maximum number of digits in a high score is important because you’re going to store that many digits for a score even if it doesn’t need that many. This makes the scores much easier to read after you’ve written them to disk. So, as an example, if the highest score is 1250, you obviously only need four digits to represent the score. However, the score needs to be stored on disk as 01250 to make it fit within a five-digit storage space. Each of the numbers in the high score list is stored this way.

Getting back to the Space Out game itself, after the high scores are read from disk, they are treated as normal integers. In fact, a simple array of integers is sufficient to represent the high score list. So, the process of reading and writing the high score list involves initializing and then storing away an array of integers. The next section gets into more detail about how exactly you use the Win32 API to read and write data using files.

Storing and Retrieving High Score Data

Before you learn about the specific Win32 API functions used to read and write files, let’s go over exactly what happens to the array of high score integers when they are read from and written to a file. You’ve already learned that each number in the array of integers gets converted to five digits. However, I didn’t mention that these digits aren’t actually numbers, but instead are text characters. In other words, the number 1250 gets converted to “01250” before being written to a file. This process is known as streaming the high score data to a file because you are converting the numeric data into a stream of characters. Figure 24.1 shows what the streaming process looks like.

Streaming the high scores involves padding the numbers to five digits and converting them to a stream of characters.

Figure 24.1. Streaming the high scores involves padding the numbers to five digits and converting them to a stream of characters.

As the figure reveals, the five high scores are converted to a stream of five-digit characters during the streaming process. Along with making it very straightforward to store the numbers to a file, streaming the numbers also makes it much easier to read them back in. More specifically, you just read five digits of characters at a time, and then convert the character string into an integer number.

I’ve talked about how to read and write files in general terms, but I haven’t given you any specifics. All file input and output in Windows involves a small set of Win32 API functions. The most important of these functions is CreateFile(), which looks like this:

HANDLE CreateFile(
  LPCTSTR szFileName,
  DWORD dwDesiredAccess,
  DWORD dwShareMode,
  LPSECURITY_ATTRIBUTES pSecurityAttributes,
  DWORD dwCreationDisposition,
  DWORD dwFlagsAndAttributes,
  HANDLE hTemplateFile);

The CreateFile() function is important because it is used to both open and close files. Although it takes quite a few arguments, not all of them apply to basic reading and writing; you don’t need to worry about some of the arguments unless you’re performing more advanced file input and output. Knowing this, I want to point out the arguments that are relevant to reading and writing high scores. The first argument, szFileName, is used to provide the name of the file to be created or opened. The second argument, dwDesiredAccess, determines what you want to do with the file, such as read from or write to it. The fifth argument, dwCreationDisposition, determines whether you want to open an existing file or create a new file. And finally, the sixth argument, dwFlagsAndAttributes, allows you to control various other aspects of opening or creating a file, such as whether you want to limit access to read only.

Rather than have you memorize the arguments to the CreateFile() function, I’d rather just show you how to use it to open and create files. Following is an example of using the CreateFile() function to create a new file ready for writing:

HANDLE hFile = CreateFile(TEXT("HiScores.dat"), GENERIC_WRITE, 0, NULL,
  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

In this example, the new file is called HiScores.dat, and it is created and ready to be written to using the file handle returned from the CreateFile() function. The hFile handle is what you’ll use with a write function later to actually write data to the file. For now, let’s take a quick look at how you open a file for reading:

HANDLE hFile = CreateFile(TEXT("HiScores.dat"), GENERIC_READ, 0, NULL,
  OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);

As you can see, the CreateFile() function is called in a similar manner as it is called for writing, with a few small changes. The second argument indicates that this is a read operation, whereas the fifth argument indicates that an existing file should be opened, as opposed to creating a new one. Finally, the sixth argument specifies that the file is read-only, which prevents you from writing to it using the provided file handle even if you wanted to.

Now that you have a file handle that can be used for reading or writing, depending on how you called the CreateFile() function, you’re ready to find out how to write data to a file and then read it back. The Win32 API function used to write data to a file is called WriteFile(), and looks like this:

BOOL WriteFile(
  HANDLE hFile,
  LPCVOID pBuffer,
  DWORD dwNumberOfBytesToWrite,
  LPDWORD pNumberOfBytesWritten,
  LPOVERLAPPED pOverlapped);

As with the CreateFile() function, not every argument to WriteFile() is critical for our purposes. You obviously need to provide the file handle in the first argument, as well as a data buffer from which the data is written. It’s also important to specify the number of bytes to write, as well as a pointer to a DWORD that is to receive the number of bytes actually written. The last argument is the one that you don’t need to worry about. To show you how easy it is to use the WriteFile() function, following is code to write a five-digit high score to a file:

DWORD dwBytesWritten;
WriteFile(hFile, &cData, 5, &dwBytesWritten, NULL);

This code assumes that the high score has already been converted from an integer into a five-character string that is stored in the cData variable. Notice that the number of bytes to write is specified as 5, which makes sense considering that the score consists of five digits.

Reading from a file is similar to writing to a file, except that it involves the ReadFile() function instead of WriteFile(). Following is the ReadFile() function as it is defined in the Win32 API:

BOOL ReadFile(
  HANDLE hFile,
  LPVOID pBuffer,
  DWORD dwNumberOfBytesToRead,
  LPDWORD pNumberOfBytesRead,
  LPOVERLAPPED pOverlapped);

As you can see, the ReadFile() function actually takes the same arguments as the WriteFile() function. However, it uses the arguments a little differently because data is being read into the buffer instead of being written from it. Following is an example of reading an individual high score from a file using the ReadFile() function:

char  cData[6];
DWORD dwBytesRead;
ReadFile(hFile, &cData, 5, &dwBytesRead, NULL);

It’s important for the array of characters that holds each score to be null-terminated, which is why the cData variable is declared as being six characters long, instead of just five. In this code, the ReadFile() function reads five characters from the file and stores them in the cData variable.

When you’re finished reading from or writing to a file, it’s important to close the file. This is accomplished with the CloseHandle() Win32 function, which is called like this:

CloseHandle(hFile);

You now have the fundamental knowledge required to store and retrieve high score data to and from a file on disk, which is what you need to add a high score feature to the Space Out game.

Building the Space Out 4 Game

The final version of the Space Out game that you create in this book is called Space Out 4, and it completes the game by including a high score list that is stored to a file on disk. You’ve learned enough about high scores and file I/O in this hour to handle the task of adding a high score list to the game. So, let’s get started!

Writing the Game Code

The Space Out 4 game requires only one new global variable, which is the array of integers that stores the high score list. Following is the _iHiScores variable as it appears in the SpaceOut.h header file:

int _iHiScores[5];

This variable is pretty straightforward in that it stores away five integers that represent the top five scores for the game. The scores are arranged in order of most to least.

Several new functions are required to successfully update, read, and write the high score list in the Space Out 4 game:

void UpdateHiScores();
BOOL ReadHiScores();
BOOL WriteHiScores();

The UpdateHiScores() function is used to determine if a new score has made it into the high score list. If so, the function makes sure to insert the score in the correct position and slide the lower scores down a notch to make room for the new score. The ReadHiScores() and WriteHiScores() functions use the _iHiScores array as the basis for reading and writing the high score list from and to the HiScores.dat file. You learn how each of these functions work a little later in the hour, but for now it’s important to see how they are used in the context of the game.

The GameStart() function is responsible for initializing the game, which now includes reading the high score list. The only change to this function is the new call to the ReadHiScores() function:

ReadHiScores();

This call makes sure that the high score list is properly initialized before the game attempts to draw the scores later in the GamePaint() function.

The GameEnd() function cleans up resources used by the game, and serves as a great place to write the high score list to a file. A simple addition to this function is all that is required to write the high score list:

WriteHiScores();

Of course, the high score list wouldn’t be of much use if the game didn’t display it for all to see. This takes place in the GamePaint() function, which is shown in Listing 24.1.

Example 24.1. The GamePaint() Function Draws the High Score List if the Game Is in Demo Mode

 1: void GamePaint(HDC hDC)
 2: {
 3:   // Draw the background
 4:   _pBackground->Draw(hDC);
 5:
 6:   // Draw the desert bitmap
 7:   _pDesertBitmap->Draw(hDC, 0, 371);
 8:
 9:   // Draw the sprites
10:   _pGame->DrawSprites(hDC);
11:
12:   if (_bDemo)
13:   {
14:     // Draw the splash screen image
15:     _pSplashBitmap->Draw(hDC, 142, 20, TRUE);
16:
17:     // Draw the hi scores
18:     TCHAR szText[64];
19:     RECT  rect = { 275, 230, 325, 250};
20:     SetBkMode(hDC, TRANSPARENT);
21:     SetTextColor(hDC, RGB(255, 255, 255));
22:     for (int i = 0; i < 5; i++)
23:     {
24:       wsprintf(szText, "%d", _iHiScores[i]);
25:       DrawText(hDC, szText, -1, &rect, DT_SINGLELINE | DT_CENTER |
26:         DT_VCENTER);
27:       rect.top += 20;
28:       rect.bottom += 20;
29:     }
30:   }
31:   else
32:   {
33:     // Draw the score
34:     TCHAR szText[64];
35:     RECT  rect = { 460, 0, 510, 30 };
36:     wsprintf(szText, "%d", _iScore);
37:     SetBkMode(hDC, TRANSPARENT);
38:     SetTextColor(hDC, RGB(255, 255, 255));
39:     DrawText(hDC, szText, -1, &rect, DT_SINGLELINE | DT_RIGHT |
40:        DT_VCENTER);
41:
42:     // Draw the number of remaining lives (cars)
43:     for (int i = 0; i < _iNumLives; i++)
44:       _pSmCarBitmap->Draw(hDC, 520 + (_pSmCarBitmap->GetWidth() * i),
45:         10, TRUE);
46:
47:     // Draw the game over message, if necessary
48:     if (_bGameOver)
49:       _pGameOverBitmap->Draw(hDC, 170, 100, TRUE);
50:   }
51: }

The high score list is drawn just after the splash screen image by looping through each score and drawing it a little below the previous score (lines 17–29). Notice that the high score list is only drawn if the game is in demo mode (line 12), which makes sense when you consider that the high score list is something you want to see in between games.

The last game function impacted by the high score list is the SpriteCollision() function, which is important because it detects when a game ends. Listing 24.2 contains the code for the SpriteCollision() function.

Example 24.2. The SpriteCollision() Function Updates the High Score List Whenever a Game Ends

 1: BOOL SpriteCollision(Sprite* pSpriteHitter, Sprite* pSpriteHittee)
 2: {
 3:   // See if a player missile and an alien have collided
 4:   Bitmap* pHitter = pSpriteHitter->GetBitmap();
 5:   Bitmap* pHittee = pSpriteHittee->GetBitmap();
 6:   if ((pHitter == _pMissileBitmap && (pHittee == _pBlobboBitmap ||
 7:     pHittee == _pJellyBitmap || pHittee == _pTimmyBitmap)) ||
 8:     (pHittee == _pMissileBitmap && (pHitter == _pBlobboBitmap ||
 9:     pHitter == _pJellyBitmap || pHitter == _pTimmyBitmap)))
10:   {
11:     // Play the small explosion sound
12:     PlaySound((LPCSTR)IDW_LGEXPLODE, _hInstance, SND_ASYNC |
13:       SND_RESOURCE);
14:
15:     // Kill both sprites
16:     pSpriteHitter->Kill();
17:     pSpriteHittee->Kill();
18:
19:     // Create a large explosion sprite at the alien's position
20:     RECT rcBounds = { 0, 0, 600, 450 };
21:     RECT rcPos;
22:     if (pHitter == _pMissileBitmap)
23:       rcPos = pSpriteHittee->GetPosition();
24:     else
25:       rcPos = pSpriteHitter->GetPosition();
26:     Sprite* pSprite = new Sprite(_pLgExplosionBitmap, rcBounds);
27:     pSprite->SetNumFrames(8, TRUE);
28:     pSprite->SetPosition(rcPos.left, rcPos.top);
29:     _pGame->AddSprite(pSprite);
30:
31:     // Update the score
32:     _iScore += 25;
33:     _iDifficulty = max(80 - (_iScore / 20), 20);
34:   }
35:
36:   // See if an alien missile has collided with the car
37:   if ((pHitter == _pCarBitmap && (pHittee == _pBMissileBitmap ||
38:     pHittee == _pJMissileBitmap || pHittee == _pTMissileBitmap)) ||
39:     (pHittee == _pCarBitmap && (pHitter == _pBMissileBitmap ||
40:     pHitter == _pJMissileBitmap || pHitter == _pTMissileBitmap)))
41:   {
42:     // Play the large explosion sound
43:     PlaySound((LPCSTR)IDW_LGEXPLODE, _hInstance, SND_ASYNC |
44:       SND_RESOURCE);
45:
46:     // Kill the missile sprite
47:     if (pHitter == _pCarBitmap)
48:       pSpriteHittee->Kill();
49:     else
50:       pSpriteHitter->Kill();
51:
52:     // Create a large explosion sprite at the car's position
53:     RECT rcBounds = { 0, 0, 600, 480 };
54:     RECT rcPos;
55:     if (pHitter == _pCarBitmap)
56:       rcPos = pSpriteHitter->GetPosition();
57:     else
58:       rcPos = pSpriteHittee->GetPosition();
59:     Sprite* pSprite = new Sprite(_pLgExplosionBitmap, rcBounds);
60:     pSprite->SetNumFrames(8, TRUE);
61:     pSprite->SetPosition(rcPos.left, rcPos.top);
62:     _pGame->AddSprite(pSprite);
63:
64:     // Move the car back to the start
65:     _pCarSprite->SetPosition(300, 405);
66:
67:     // See if the game is over
68:     if (--_iNumLives == 0)
69:     {
70:       // Play the game over sound
71:       PlaySound((LPCSTR)IDW_GAMEOVER, _hInstance, SND_ASYNC |
72:         SND_RESOURCE);
73:       _bGameOver = TRUE;
74:       _bGameOverDelay = 150;
75:
76:       // Update the hi scores
77:       UpdateHiScores();
78:     }
79:   }
80:
81:   return FALSE;
82: }

When a game ends, the SpriteCollision() function plays a game over sound and sets the game over delay, which you already know about (lines 71–74). However, what you don’t already know about is the line of code that calls the UpdateHiScores() function to give the game a chance to insert the new score in the high score list (line 77). You don’t have to worry about whether the score is high enough to make the high score list because this is determined by the UpdateHiScores() function, which is shown in Listing 24.3.

Example 24.3. The UpdateHiScores() Function Checks to See if a High Score Should Be Added to the High Score List, and Adds It if Necessary

 1: void UpdateHiScores()
 2: {
 3:   // See if the current score made the hi score list
 4:   int i;
 5:   for (i = 0; i < 5; i++)
 6:   {
 7:     if (_iScore > _iHiScores[i])
 8:       break;
 9:   }
10:
11:   // Insert the current score into the hi score list
12:   if (i < 5)
13:   {
14:     for (int j = 4; j > i; j--)
15:     {
16:       _iHiScores[j] = _iHiScores[j - 1];
17:     }
18:     _iHiScores[i] = _iScore;
19:   }
20: }

Although the code for the UpdateHiScores() function looks a little tricky at first, it really isn’t too bad. The first loop checks to see if the score is higher than any of the existing high scores (lines 5–9). If so, the second loop is entered, which handles the task of inserting the score in the correct position in the list, as well as sliding down the lower scores in the list (lines 12–19). You might notice that the list is looped through in reverse, which allows it to easily move scores down the list to make room for the new score (lines 14–17). After a space has been made, the new score is placed in the high score list (line 18).

The high scores are written to a data file in the WriteHiScores() function, which is shown in Listing 24.4.

Example 24.4. The WriteHiScores() Function Writes the High Score List to the File HiScores.dat

 1: BOOL WriteHiScores()
 2: {
 3:   // Create the hi score file (HiScores.dat) for writing
 4:   HANDLE hFile = CreateFile(TEXT("HiScores.dat"), GENERIC_WRITE, 0, NULL,
 5:     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 6:   if (hFile == INVALID_HANDLE_VALUE)
 7:     // The hi score file couldn't be created, so bail
 8:     return FALSE;
 9:
10:   // Write the scores
11:   for (int i = 0; i < 5; i++)
12:   {
13:     // Format each score for writing
14:     CHAR cData[6];
15:     wsprintf(cData, "%05d", _iHiScores[i]);
16:
17:     // Write the score
18:     DWORD dwBytesWritten;
19:     if (!WriteFile(hFile, &cData, 5, &dwBytesWritten, NULL))
20:     {
21:       // Something went wrong, so close the file handle
22:       CloseHandle(hFile);
23:       return FALSE;
24:     }
25:   }
26:
27:   // Close the file
28:   return CloseHandle(hFile);
29: }

Most of this code should look familiar to you because it is very similar to the code you saw earlier in the hour when discussing how to create a new file and write a score using the CreateFile() and WriteFile() functions. A new HiScores.dat file is first created with a call to the CreateFile() function (lines 4–8); even if the file already exists, a new one is created to replace it. After the file is created, the function prepares to write to the file by looping through the high scores (line 11). Each score must be formatted into a five-digit character string before it can be written (lines 14 and 15). The score is then written to the file with a call to the WriteFile() function (line 19). If an error occurs during the write, the file handle is closed with a call to CloseHandle() (line 22). If all goes well and the data is successfully written, the function finishes by closing the file with a call to CloseHandle() (line 28).

Not surprisingly, the ReadHiScores() function works similarly to WriteHiScores(), except everything happens in the reverse. Listing 24.5 contains the code for the ReadHiScores() function.

Example 24.5. The ReadHiScores() Function Reads the High Score List from the File HiScores.dat

 1: BOOL ReadHiScores()
 2: {
 3:   // Open the hi score file (HiScores.dat)
 4:   HANDLE hFile = CreateFile(TEXT("HiScores.dat"), GENERIC_READ, 0, NULL,
 5:     OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
 6:   if (hFile == INVALID_HANDLE_VALUE)
 7:   {
 8:     // The hi score file doesn't exist, so initialize the scores to 0
 9:     for (int i = 0; i < 5; i++)
10:       _iHiScores[i] = 0;
11:     return FALSE;
12:   }
13:
14:   // Read the scores
15:   for (int i = 0; i < 5; i++)
16:   {
17:     // Read the score
18:     char  cData[6];
19:     DWORD dwBytesRead;
20:     if (!ReadFile(hFile, &cData, 5, &dwBytesRead, NULL))
21:     {
22:       // Something went wrong, so close the file handle
23:       CloseHandle(hFile);
24:       return FALSE;
25:     }
26:
27:     // Extract each integer score from the score data
28:     _iHiScores[i] = atoi(cData);
29:   }
30:
31:   // Close the file
32:   return CloseHandle(hFile);
33: }

The ReadHiScores() function reads the high score list from the HiScores.dat file. The CreateFile() function is again used to obtain a file handle, but this time an existing file is opened, as opposed to creating a new file (lines 4 and 5). If there is an error reading the file, such as if the file doesn’t exist, the high score array is simply filled with scores of 0 (lines 9 and 10). This code is very important because the first time you play the game, there won’t be a high score file.

If the file is opened okay, the function starts a loop to read each score from the file (line 15). Each score is then read by calling the ReadFile() function (line 20). Simply reading the scores from the file isn’t sufficient to place the score in the high score list because the scores are read as five-digit character strings. Each score must be converted to an integer number before you can add it to the high score array (line 28). After reading and converting all the scores, the ReadFile() function finishes by calling the CloseHandle() function to close the file (line 32).

Testing the Finished Product

Similar to a few of its predecessors, the Space Out 4 game is quite simple to test. In fact, it’s also a fun test to perform because you need to play the game a few times and build up some scores on the high score list. Figure 24.2 shows the high score list in the game upon playing the game for the first time, which means that the list is filled with scores of 0.

Prior to playing the Space Out 4 game for the first time, the high score list is full of zero scores.

Figure 24.2. Prior to playing the Space Out 4 game for the first time, the high score list is full of zero scores.

Keep in mind that in this figure the game still tried to read the high scores from a file, but the file didn’t exist, so the scores were zeroed out. After you play a few games, the list will start looking better as it fills out with new high scores. Whenever you exit the game, the high score list gets stored to a file. Upon restarting the game, the high score list is restored by reading the scores from the same file. Figure 24.3 shows a high score list that has just been restored from a file.

The high score list was read from a file, which causes it to persist between games.

Figure 24.3. The high score list was read from a file, which causes it to persist between games.

The high score list really is a neat enhancement to the Space Out game because it allows you to keep track of the best games you’ve played, potentially over a long period of time. Keep in mind that you can easily clear out the high score list by simply deleting the HiScores.dat file.

Summary

This hour explored a facet of games that you might have overlooked, the high score list. Granted, high score lists aren’t as popular these days because arcades aren’t as important as they were in the early days of video games, but it doesn’t necessarily mean that players don’t like to know what their highest gaming achievements are. High score lists are a great way to remember the best games you’ve played, and also serve as a way for competitive gamers to share a high score with friends. Although a high score list isn’t necessary in all games, you should consider adding it to games where it makes sense. The Space Out game is a good example of a game that benefits from keeping track of high scores.

This hour is the last hour in the book, and therefore serves as your send-off into the expansive world of game programming. Hopefully you’ve had fun learning the ins and outs of game construction, and are excited to embark on some game development projects of your own. Have fun!

Q&A

Q1:

What is the purpose of a file handle?

A1:

Similar to other handles in Windows, such as window handles and bitmap handles, a file handle is a reference to a location in memory that stores information about a file. More importantly, a file handle serves as your interface to a file, and is required in all file operations. You can think of a file handle as working a lot like a key for a locker in an airport terminal. When you open the file, you receive the handle (key) that you must then use in order to read from or write to the file. When you’re finished with the file, you close the file, which returns the handle to Windows.

Q2:

Why exactly can’t I read and write the scores as integers in the Space Out 4 game?

A2:

The reason for having to convert the integer scores to characters has to do with the fact that characters are a little easier to manipulate when it comes to reading and writing files. Because the number of digits in a score actually varies, it becomes difficult to parse a number from a data file if it is stored as a raw integer. Therefore, it’s easier to convert the integers into readable text that can be read from and written to files a character at a time. A side benefit of this approach is that you can open the HiScores.dat file and see the scores. Of course, this could also be perceived as a drawback because technically people could cheat and change the scores in the data file, but only you know this secret!

Workshop

The Workshop is designed to help you anticipate possible questions, review what you’ve learned, and begin learning how to put your knowledge into practice. The answers to the quiz can be found in Appendix A, “Quiz Answers.”

Quiz

1:

What is the name of the Win32 function used to open and create files?

2:

How do you read and write data using files in Windows?

3:

How do you clear the high score list in the Space Out 4 game?

Exercises

  1. Try opening the HiScores.dat file in a text editor and modifying the high score list by hand. Although this isn’t a very honest way to register a high score, you might have some fun tweaking a high score to play a trick on a friend.

  2. Modify the Space Out 4 game so that it includes more than the top five scores in the high score list. You’ll need to modify the splash screen image to make room for the new scores, as well as modify the storage of the scores, and how they are read from and written to a file.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.79.65