This tech tip adds a new high score category to P3SampleApp and covers some high score implementation details.
High scores are well documented in the Persistent Data section of the SDK Developer Guide.
The new high score will reflect how many times the lower lanes have been completed. To implement the new high score we need to create a HighScoreCategory in P3SAHighScoreCategories and add it to the list returned by GetCategories().
Edit Assets\Scripts\Modes\DataManagement\P3SAHighScoreCategories.cs, change GetCategories() as shown and add the LowerLanes() method.
public static List<HighScoreCategory> GetCategories() { List<HighScoreCategory> cats = new List<HighScoreCategory>(); cats.Add(Score()); cats.Add(Shots()); cats.Add(LowerLanes()); return cats; } private static HighScoreCategory LowerLanes() { HighScoreCategory hsCat = new HighScoreCategory("NumLaneCompletions", "Lower Lanes", HIGH_SCORE_COUNT); List<double> values = new List<double>(); double startingValue = HIGH_SCORE_COUNT; for (int i = 0; i < HIGH_SCORE_COUNT; i++) { values.Add(startingValue - i); } hsCat.SetDefaultValues(values); return hsCat; } |
As specified by the first argument of the HighScoreCategory constructor, this high score category will take the player’s score value from NumLaneCompletions in the player’s data. LanesMode already stores the number of lower lane completions in the player’s data with that key. This is done with the following statement:
data.currentPlayer.SaveData("NumLaneCompletions", numCompletions); |
In this case, the player's data was already populated by an existing mode. In general, you will need to implement the code to save the category score in the player’s data.
After making the changes above, click the Play button in Unity. Start a game by pressing s. Launch the first ball by pressing l (lowercase L). Drag the mouse over the lower lanes to complete the set of 4 lower lanes at least twice. Drain the ball by pressing 0 (zero). Launch and drain balls 2 and 3. Enter your name for the Lower Lanes high score.
When in Attract mode, you can use the main flipper buttons to display the high score categories immediately. The right flipper button moves forward, and the left flipper button moves backward through the categories.
You can specify the number of places for a HighScoreCategory in the 3rd argument of the constructor. In the lower lanes high score category, we passed the constant HIGH_SCORE_COUNT which is equal to 10.
It is recommended to keep numPlaces at 10 or less. If numPlaces is >10, then the high score display in Attract mode is broken with names appearing below the window box. Displaying this properly would require reimplementing HighScoreResultsMode.
The defaultNames are the names of the players for the initial high scores. If the defaultNames are not specified, the HighScoreCategory class picks: { "G S", "J W", "R C", "L P", "T W", "T J", "S G", "D T", "N P", "S S", "T L" }. You can specify your own defaultNames with SetDefaultNames. For example:
hsCat.SetDefaultNames(new List<string>() { "BEN", "BOB", "EVE", "IAN", "JAY", "JIM", "JOE", "KEN", "SAM", "TOM" });
If numPlaces is less than the number of default names, then the extra names are ignored. If numPlaces is greater than the number of defaultNames, then the name "A A" is used to fill the missing names.
The defaultValues are the values for the initial high scores. Those are the values you have to beat to be allowed to enter your name in the high score table. By default, all defaultValues are set to 0. We showed how to set the defaultValues with SetDefaultValues() in the LowerLanes() method above.
If numPlaces is less than the number of default values, then the extra values are ignored. If numPlaces is greater than the number of defaultValues, then the last value is duplicated to fill the missing values.
The zero value is (conceptually) the score the player has at the start of the game. The default zeroValue is 0. Notice there is a single zeroValue. Compare that to the number of default values controlled by numPlaces.
The default for sortUp is false. This is the usual case where the best score is the highest score. The score decreases as the positions increase. Sometimes you want the opposite. For example, when the score is the time it takes to finish an objective. Finishing faster is better. In that case, change the sort order with SetSortUp(true) to let the score increase as the positions increase.
When sortUp is true, you want the player to start with a bad score, therefore the zeroValue should be high, certainly not 0.
The default decimalPlaces is 0. This displays the floating-point high score as an integer. Set this value to the number of digits to the right of the decimal point. For example, if the score represents a time interval, you might want to call SetDecimalPlaces(2) to display up to 1/100th of a second.
Players on a team are not eligible to receive a high score. Team members combining their scores would have an unfair advantage.
By default, players playing a game restored from a savepoint are not eligible to receive a high score. This makes sense for values that accrue over the span of the whole game, like the final score or the number of aliens killed.
An application can allow high scores from restored games for values that are not affected by the number of balls, like the time to complete a hurry-up. This can be done by calling DisallowFromRestoredGames(false) on the HighScoreCategory.
To bring up the Statistics menu, open the coin door and press the launch button, then select Statistics.
“Enable high score display” determines whether the high scores are shown in Attract mode.
Selecting “Reset scores to defaults” brings up a confirmation dialog. If you select Yes, this will erase all user high scores for this application and set them back to the initial high scores as determined by defaultNames and defaultValues.
Selecting “Clear high scores” brings up a confirmation dialog. If you select Yes, this will erase all user high scores for this application and set them all to the zeroValue. This score is meant to be so bad that the next player is sure to enter their name for a high score, and the next player after that, until all high score positions are filled again.
High scores are stored in GameAttributes to make them persistent.
The number of places in the high score category is how many entries are kept for this high score. If there are 10 places, up to 10 players will be able to enter their names to immortalize their scores.
If the high score category has numPlaces entries, this will create numPlaces GameAttributes named HS_<description><N> where <N> goes from 0 to numPlaces-1. For example, the lower lanes high score category has 10 places and the description is “Lower Lanes”, so the GameAttributes will be named “HS_Lower Lanes0” to “HS_Lower Lanes9”. Notice the space character in the name is perfectly acceptable.
The value of the high score GameAttribute has the format "<name>:<score>" where <name> is the name the player entered and <score> is the actual score achieved. For example, the value could be GSS:1,000.0000000000 which means GSS got a high score when scoring 1000.
All these internal details are taken care of by HighScoreResultsMode when displaying the current high score values.
High scores are stored internally as double values before being converted to strings. The double data type provides sufficient precision to represent any practical score exactly.
High score GameAttributes are stored in <userhome>/.multimorphic/P3/Data/<AppCode>/HighScores.db
This is determined by HighScoresMode, i.e., the base class of P3SAHighScoresMode. In P3SAHighScoresMode there is almost nothing except the database version for the HighScores.db file.
We changed the GameAttributes by adding a new high score category; therefore, we might want to increase the DBVersion. Be careful with that because this will revert to the default high scores, wiping out all existing user high scores for this application.
If you decide to change the DBVersion, it is always a good idea to keep both DBVersion and DataVersion at the same value. Since DataVersion is 7 and DBVersion is 4 in P3SampleApp, a reasonable choice would be to increase both of them to 8.
This tutorial does not increase DBVersion in order to preserve existing high scores. Increasing DBVersion is not absolutely necessary in this case since we are only adding new GameAttributes.
Notice the member “private List<HighScoreCategory> categories;” in P3SAHighScoresMode is dead code. It can safely be removed. This is not how HighScoresMode gets the list of categories.
In reality, P3SAGameAttributeManagerMode calls P3SAHighScoreCategories.GetCategories() and posts the Evt_AddHighScoreCategory event for each category. HighScoresMode handles that event to recreate the list of categories.