An XNA Top Scores program

Today we are going to write the code to maintain a top ten list. We will have to:

  1. Read the current top ten names and scores from a file.
  2. Add a new name and score to the list.
  3. Sort the list.
  4. Write the sorted list back out to the file.

All of the things on the above list are done once.

  1. Reading the current top ten needs to be done once before everything else.
  2. Adding a new name needs to be done once after the game has been played (although in this program there is no game to be played, so we'll just do it right away.).
  3. Sorting needs to be done once after the new name and score have been added to the listat the end of the game.
  4. Writing the sorted list back to the file needs to be done onceafter the data has been sorted.

Create a new XNA project called Day19TopScores. A game program would need to keep track of the top scores. Assume that we already have a Top Scores file out there and we are going to keep track of the top 10 scores. Create a file in NotePad. Name the file Top10.txt. Each name is padded with enough blanks to make it 10 characters long.

Nobody 10

Nobody 20

Nobody 30

Nobody 40

Nobody 50

Nobody 60

Nobody 70

Nobody 80

Nobody 90

Nobody 100

Save it in your bin/x86/debug folder of your Day19TopScores project.

Our program will have the following states:

  1. Splash (read the file and display the top 10)
  2. GetName
  3. GetScore
  4. Playing
  5. GameOver
  1. Add current name and score to the list (once)
  2. Sort the list (once)
  3. Write the list back out to the file (once)

We need to:

  1. Read in the top scores file in Initialize.
  2. When in Splash, display the top scores in Draw every time.
  3. When user presses Start, switch to the GetName state.
  4. When in the GetName state get the player's name.
  5. When the player's name has been read, switch to the GetScore state.
  6. When in the GetScore state, get the player's score.
  7. When the player's score has beenread, switch to the Playing state.
  8. When the user presses the Start button on the gamepad, switch to the GameOver state.
  9. When in the GameOver state, sort the new data.
  10. When done sorting the data, write it out to the data file.
  11. When in the GameOver state, display the new top 10 on the screen on every tick.
  12. When the user presses the Back button, end the program.

I used the following variables:

IAsyncResult keyboardAsyncResult; //*********************************

bool nameRequested, scoreRequested; //*********************************

bool listSorted, fileWritten;

GamePadState gamePad, prevGamePad;

enumGameStates

{

Splash,

GetName,

GetScore,

Playing,

GameOver

}

GameStates gameState;

structPlayerStruct

{

publicstring PlayerName;

publicint PlayerScore;

}

constint maxPlayers = 11;

PlayerStruct[] player;

StreamReader inFile;

StreamWriter outFile;

SpriteFont font;

Note that if we are going to read a file and write a file, we need to add the following:

using System.IO;

The Initialize method

player = newPlayerStruct[maxPlayers];

nameRequested = false;

scoreRequested = false;

listSorted = false;

fileWritten = false;

ReadFile(player);

gameState = GameStates.Splash;

A procedure to read the top scores

To read in the top scores file. We have an array with room for 11 (0 through 10) scores even though we only keep track of the top 10. This will allow us to add the current player's score to the list (in the last position, index=10).

Note:

  • The use of Substring, Trim, and Convert.ToInt32.

void ReadFile(PlayerStruct[] list)

{

inFile = newStreamReader("Top10.txt");

for (int i= 0; i < maxPlayers - 1; i++)

{

string line = inFile.ReadLine();

player[i].PlayerName = line.Substring(0, 10).Trim();

player[i].PlayerScore = Convert.ToInt32(line.Substring(10));

}

player[maxPlayers - 1].PlayerName = "JUNK ";

player[maxPlayers - 1].PlayerScore = 0;

inFile.Close();

}

Before we write the methods to get input from the player, we will write some other methods (that are easier to write).

Add code to call the following from within the Draw method and test your program.

A procedure to display the top 10:

NOTE:

  • Output is padded for alignment. Note that this only works with a monospaced font like Courier New.

void DisplayList(PlayerStruct[] list)

{

Vector2 nameVector = newVector2(0, 0);

float lineHeight = font.MeasureString("X").Y;

for (int i = 0; i < maxPlayers; i++)

{

nameVector.Y += lineHeight;

spriteBatch.DrawString(font, player[i].PlayerName.PadRight(15) +

player[i].PlayerScore.ToString(), nameVector, Color.White);

}

}

The following method will sort the data. Since this only needs to be done once, call it from Initialize. Now, when the list is displayed, it will be sorted.Test your program.

A procedure to sort the data (Bubble Sort)

Sort the data in descending order.

void SortList(PlayerStruct[] list)

{

PlayerStruct save;

int count = list.GetLength(0);

for (int i = 1; i < count; i++)

{

for (int j = count - 1; j >= 1; j--)

if (list[j - 1].PlayerScore < list[j].PlayerScore)

{

save = list[j - 1];

list[j - 1] = list[j];

list[j] = save;

}

}

}

The following method will write the data back out to a file (in this case, it is a different file, NewTop10.txt). Add code to call this method from Initialize after you have sorted the data. Test your program.

A procedure to write to a file

Things to note:

  • We only write the top 10, even though there are 11 in the array.

void WriteFile(PlayerStruct[] list)

{

outFile = newStreamWriter("NewTop10.txt");

for (int i = 0; i < maxPlayers - 1; i++)

{

outFile.Write(player[i].PlayerName.PadRight(10));

outFile.WriteLine(player[i].PlayerScore);

}

outFile.Close();

}

A procedure to display an input box on the screen and read the name:

Now we get to the interesting part.Remove the calls in Initialize that sort the data and write it back out to a file. We will modify our program and get the name and score from the user, and then sort and write the data when in the GameOver state.

If we are going to use an input box to get input from the user, we will need to add the following to the Game1 method:

Components.Add(newGamerServicesComponent(this));

We need two methods to read the player's name.RequestName displays the input box on the screen. GetName reads the name after the user has entered his name and clicked on the Submit button. Add the following and test your program.

void RequestName()

{

if (keyboardAsyncResult == null)

{

keyboardAsyncResult = Guide.BeginShowKeyboardInput(

PlayerIndex.One, "Name", "Enter player name ",

"", newAsyncCallback(GetName), null);

}

}

void GetName(IAsyncResult result)

{

// input box HAS been displayed

if (keyboardAsyncResult != null

& keyboardAsyncResult.IsCompleted)// user is done with input box

{

// Get what the user typed

string name = Guide.EndShowKeyboardInput(keyboardAsyncResult).Trim();

if (name != null)

{

player[maxPlayers - 1].PlayerName = name;

keyboardAsyncResult = null;

gameState = GameStates.GetScore;

}

}

}

We need to call GetName at the appropriate time. The appropriate time is when we move into the GetName state. Add the following to the Update method:

gamePad = GamePad.GetState(PlayerIndex.One);

bool startPressed = gamePad.Buttons.Start == ButtonState.Pressed

& prevGamePad.Buttons.Start == ButtonState.Released;

if (startPressed)

{ if (gameState == GameStates.Splash)

gameState = GameStates.GetName;

elseif (gameState == GameStates.Playing)

gameState = GameStates.GameOver;

}

if (gameState == GameStates.GetName)

{

if (!nameRequested)

{

nameRequested = true;

RequestName();

}

}

A procedure to display an input box on the screen and read the score:

We also need two methods to read the player's score. RequestScore displays the input box on the screen. GetScore reads the score after the user has entered his score and clicked on the Submit button. Add the following and test your program.

void RequestScore()

{

if (keyboardAsyncResult == null)

{

keyboardAsyncResult = Guide.BeginShowKeyboardInput(

PlayerIndex.One, "Name", "Enter player score: ",

"", newAsyncCallback(GetScore), null);

}

}

void GetScore(IAsyncResult result)

{// input box HAS been displayed

if (keyboardAsyncResult != null

& keyboardAsyncResult.IsCompleted)// user is done with input box

{

// Get what the user typed

string score = Guide.EndShowKeyboardInput(keyboardAsyncResult).Trim();

if (score != null)

{

player[maxPlayers - 1].PlayerScore = Convert.ToInt32(score);

keyboardAsyncResult = null;

gameState = GameStates.Playing;

}

}

}

Update

We need to make sure that we call the GetScore method at the appropriate time, too. Add the following to Update (below the code you added previously). This will request the score.Test your program.

if (gameState == GameStates.GetScore)

{

if (!scoreRequested & Guide.IsVisible == false)

{

scoreRequested = true;

RequestScore();

}

}

Sorting and Writing at the end

Add the following to the end of Update (below the last code that you added). Test your program.

if (gameState == GameStates.GameOver)

{

if (!listSorted)

{

SortList(player);

listSorted = true;

}

if (!fileWritten)

{

WriteFile(player);

fileWritten = true;

}

}

Displaying the appropriate data

Write the Draw method so that it will display the top 10 at the beginning of the program (Splash) and again at the end of the program (GameOver).

spriteBatch.Begin();

if (gameState == GameStates.Splash)

DisplayList(player);

elseif (gameState == GameStates.GameOver)

DisplayList(player);

spriteBatch.End();

10/22/2018Day19--An XNA Top Scores programFULL.docxPage 1 of 7