Storing Player Name
How could we modify the high score example to also store the player's name along with the score?
To store the player's name along with the score, you'll need to define a structure that holds both the name and the score. You'll also need to decide on a format for storing this data in the file.
Let's see an example. First, we'll define a structure:
#include <SDL.h>
#include <iostream>
#include <algorithm>
#include <string>
struct ScoreEntry {
int32_t Score;
// Assuming a maximum name length
// of 49 characters + null terminator
char Name[50];
};
Next, let's modify UpdateHighScore()
:
void UpdateHighScore(
const char* Path, const ScoreEntry& NewEntry
) {
SDL_RWops* File{SDL_RWFromFile(Path, "w+b")};
if (!File) {
std::cerr << "Error opening file: "
<< SDL_GetError() << "\n";
return;
}
const int NumScores{5};
ScoreEntry HighScores[NumScores];
size_t EntriesRead{SDL_RWread(
File, HighScores,
sizeof(ScoreEntry),
NumScores
)};
if (EntriesRead < NumScores) {
std::cout << "Initializing new high scores\n";
for (size_t i{EntriesRead}; i<NumScores; ++i) {
HighScores[i].Score = 0;
// Initialize name to an empty string
HighScores[i].Name[0] = '\0';
}
}
bool IsHighScore{false};
for (int i{0}; i < NumScores; ++i) {
if (NewEntry.Score > HighScores[i].Score) {
IsHighScore = true;
break;
}
}
if (IsHighScore) {
std::cout << NewEntry.Name
<< " achieved a new high score of "
<< NewEntry.Score
<< "! Updating file\n";
HighScores[NumScores - 1] = NewEntry;
std::sort(HighScores, HighScores + NumScores,
[](const ScoreEntry& a, const ScoreEntry& b) {
return a.Score > b.Score;
});
SDL_RWseek(File, 0, RW_SEEK_SET);
SDL_RWwrite(
File, HighScores,
sizeof(ScoreEntry), NumScores
);
} else {
std::cout << "Not a high score - "
"file not updated\n";
}
SDL_RWclose(File);
}
int main() {
ScoreEntry ScoreOne{500, "Noob"};
UpdateHighScore("highscores.dat", ScoreOne);
ScoreEntry ScoreTwo{5000, "Alice"};
UpdateHighScore("highscores.dat", ScoreTwo);
ScoreEntry ScoreThree{3000, "Bob"};
UpdateHighScore("highscores.dat", ScoreThree);
ScoreEntry ScoreFour{6000, "Charlie"};
UpdateHighScore("highscores.dat", ScoreFour);
ScoreEntry ScoreFive{4000, "David"};
UpdateHighScore("highscores.dat", ScoreFive);
ScoreEntry ScoreSix{5500, "Eve"};
UpdateHighScore("highscores.dat", ScoreSix);
return 0;
}
Initializing new high scores
Noob achieved a new high score of 500! Updating file
Alice achieved a new high score of 5000! Updating file
Bob achieved a new high score of 3000! Updating file
Charlie achieved a new high score of 6000! Updating file
David achieved a new high score of 4000! Updating file
Eve achieved a new high score of 5500! Updating file
Explanation
- We define a
ScoreEntry
struct to hold the player's name and score. - We use a fixed-size character array (
char Name[50]
) for the name, assuming a maximum length of 49 characters (plus the null terminator). - In
UpdateHighScore()
, we now read and write an array ofScoreEntry
structs. - When initializing new entries, we set the
Name
field to an empty string. - When a new high score is achieved, we add the
NewEntry
to theHighScores
array and sort the array based on theScore
member using a lambda function for comparison. - We then write the entire sorted
HighScores
array back to the file.
Considerations
- Fixed-Length Names: Using a fixed-size character array for the name simplifies the code but limits the length of names. You might consider using a more flexible approach, like storing the length of the name followed by the characters, but that would add complexity to the file format.
- Error Handling: You might want to add error handling to deal with cases where the name is longer than the allowed length.
- Alternative Data Formats: For more complex data structures, you might consider using a serialization library or a standard data format like JSON or XML, but that's beyond the scope of this simple example.
This example demonstrates a basic way to store both the player's name and score in the high score file. You can adapt this approach to handle more complex data structures or use different file formats based on your specific needs.
Read/Write Offsets and Seeking
Learn how to manipulate the read/write offset of an SDL_RWops
object to control stream interactions.