-
Notifications
You must be signed in to change notification settings - Fork 1
End Game Statistics Programming Guide
The game has a Statistics Tracking System, which works off of the events. It utilises the GameState and SaveHandler class to maintain stats through play sessions. Upon starting a new game, or dying all stats are wiped
The system is made up of the StatManager
, StatDisplay
and Stat
classes. The Stat
class handles stat initialisation and incrementation methods. The StatManager
class handles subscribing to listeners and firing triggers for incrementing stats. The StatDisplay
creates the stat logbook, displaying end game stats to the player.
Each stat has a statName that is used as the string for events listeners and a description that is used to display the stat information to the player. A stat currently holds one value, a type and an optional max value and provides a handler to update the value on an event being triggered.
Stat
has the following constructor
public Stat(String statName, String description, int current, Integer statMax, Boolean hasMax, StatType type) {
this.statName = statName;
this.statDescription = description;
this.current = current;
this.statMax = statMax;
this.hasMax = hasMax;
this.type = type;
}
Stats can be instantiated by adding them to assets/defaultsaves/stats.json
file :
[
{
"statName": "AppleCollected",
"statDescription": "Apples collected by player",
"statCurrent": 0,
"statHasMax": false,
"statMax": null,
"type": "ITEM"
},
]
Stats are added using the StatManager
component in the player factory
player.addComponent((new StatManager(player)));
Stats are written to and read from json using the StatSave class within GameState.
public void write(Json json) {
json.writeArrayStart("stats");
for(Stat element : stats) {
json.writeObjectStart();
json.writeType(element.getClass());
json.writeValue("statName", element.getStatName());
json.writeValue("statDescription", element.getStatDescription());
json.writeValue("statCurrent", element.getCurrent());
json.writeValue("statHasMax", element.getStatMax()!=-1);
json.writeValue("statMax", element.getStatMax());
json.writeValue("type", element.getType());
json.writeObjectEnd();
}
json.writeArrayEnd();
}
public void read(Json json, JsonValue jsonData) {
JsonValue.JsonIterator iterator = jsonData.child.iterator();
while(iterator.hasNext()) {
JsonValue value = iterator.next();
Stat newStat = new Stat(
value.get("statName").asString(),
value.get("statDescription").asString(),
value.get("statCurrent").asInt(),
value.get("statMax").asInt(),
value.get("statHasMax").asBoolean(),
Stat.StatType.valueOf(value.get("type").asString())
);
stats.add(newStat);
}
}
Stats are Saved through the SaveHandler, and can be loaded in the same way. Currently stats are saved once a stat is incremented and when the player exits the game.
SaveHandler.getInstance().save(GameState.class, "saves", FileLoader.Location.LOCAL);
They are saved to assets/saves/stats
. These stats are used for tracking until the file is manually reset.
[
{
class: com.csse3200.game.components.stats.Stat
statName: AppleCollected
statDescription: Apples collected by player
statCurrent: 5
statMax: null
statHasMax: false
type: ITEM
}
]
The StatManager
integrates with the EventService by registering event listeners for each stat using its statName. When an event with the matching name is triggered, incrementStat
is invoked, modifying the corresponding stat based on the provided operation.
Stats are managed within the StatManager. Firstly they are setup and a listener (example using item collection)
void setupStats() {
// Event for defeating an enemy
player.getEvents().addListener("addItem", this::handleCollection);
for (Stat stat : stats) {
subscribeToStatEvents(stat);
}
}
Then a listener is added for the stat name which is made up of 'item.getName()' + "Collected"
private void subscribeToStatEvents(Stat stat) {
player.getEvents().addListener(stat.getStatName(), () -> this.incrementStat(stat.getStatName(), "add", 1));
}
If an "addItem" event is triggered handle the collection of an item by triggering the subscribeToStatEvents listener
void handleCollection(AbstractItem item){
player.getEvents().trigger(item.getName() + "Collected");
}
Stats are displayed at the end of the game, following the defeat of the final Kangaroo Boss. This is achieved within the StatDisplay
class using the GDXLib table ui system.
StatManager
uses logging to provide information when updating stats. This is for debugging and tracking operations during gameplay:
logger.info("Updating {} with {} to {}", stat.getStatName(), operation, value);
If a stat does not exist, a log entry is generated:
logger.info("stat not found in stats");
package: com.csse3200.game.components
-
Seeing getStatName()
: returns name of the Stat. -
void getStatDescription()
: returns the description of the stat. -
int getCurrent()
: returns the current stat value. -
Integer getMax()
: if it exists, returns the max stat value. -
boolean hasMax()
: returns true if the stat has a max value. -
void setCurrent(int value)
: sets the current value of the stat by a value. -
void addValue(int experience)
: adds a value to the current stat. -
void subtractValue(int value)
: subtract a value from the current stat. -
void update(String operation, int value)
: update the Stat based on the specified operation and value. -
Stat.StatType getType()
: get the type of stat.
-
public void write(JSON json)
: write values into json -
public void read(JSON json)
: read values from json
Note: No stat can go below a value of 0.
-
Stat
Class:- The
Stat
class is thoroughly unit tested, with 100% method coverage and 94% line coverage. - The following functionalities are covered by the tests:
- Constructor validation, ensuring the correct instantiation of stat attributes (
statName
,statDescription
,current
,max
,hasMax
, andtype
). -
getCurrent()
,getMax()
,hasMax()
methods for retrieving stat values and max limits. - The
setCurrent()
,addValue()
, andsubtractValue()
methods to ensure stat values are correctly updated, including boundary cases like exceeding max values and reducing below zero. -
update()
method to handle different operations (set
,add
,subtract
) and confirm stat updates. - JSON serialisation and deserialisation (
write(Json)
andread(Json, JsonValue)
), ensuring that stats are correctly saved and loaded. - The
toString()
method to ensure a correct string representation of the stat object.
- Constructor validation, ensuring the correct instantiation of stat attributes (
- The
- There is no significant code duplication observed in the
Stat
class, as the focus remains on ensuring each method functions independently and covers its intended functionality.
-
Stat Manager and Stat Display Integration:
-
Integration testing for the
StatManager
andStatDisplay
components was primarily done through playtesting and visual validation. -
Focus: Ensuring the following:
- StatManager properly tracks and updates statistics during gameplay based on events (e.g., item collection or combat events).
- StatDisplay correctly renders the statistics at the end of the game, after defeating the final boss, showing the right values for collected items, defeated enemies, and player stats.
-
Approach: Integration testing was performed by simulating real gameplay, where item collection events and combat events trigger stat updates. The final boss defeat scenario tested the correct display of all accumulated statistics.
-
-
Objective: Play testing was performed to ensure the
StatManager
,StatDisplay
, andStatSaveManager
work cohesively during a full playthrough, especially during combat and after defeating the final boss. -
Areas of Focus:
- Stat Updates: Ensuring stats like "items collected", "enemies defeated", and "player actions" were updated correctly during gameplay and reflected in the UI.
- Stat Display: Verifying that the end-game display shows accurate and readable statistics after defeating the final boss.
- Saving and Loading: Ensuring stats were accurately saved to and loaded from the save file during different game sessions.