Home > other >  What's the best way to store a finite number of "stats"?
What's the best way to store a finite number of "stats"?

Time:12-17

this might be a dumb question. But I'm fairly certain there has to be a better way to get and set values for a finite number of stats such that they can easily be presented in a list.

So I have a game and I want to display a list (or sublist) of "stat name" "stat value" where the stat name needs to be localized and the value can either be a int, float or string.

So here is an example of the code I have. In C# (for Unity)

[Serializable]
public enum GameStatisticsEnum
{
    TimesGameOpened,
    TimePlayed,
    TimeBattling,
    VersusBattleCount,
    VersusRoundCount,
    SpellCastCount,
    DeathCount,
}

[Serializable]
public class GameStatistics
{
    [SerializeField] public int TimesGameOpened;
    [SerializeField] public float TimePlayed;
    [SerializeField] public float TimeBattling;
    [SerializeField] public int VersusBattleCount;
    [SerializeField] public int VersusRoundCount;
    [SerializeField] public int SpellCastCount;
    [SerializeField] public int DeathCount;
    [SerializeField] public ZoneStats[] ZonesStats;
    [SerializeField] public CharacterStats[] CharactersStats;

    public string GetStatLocalizationID(GameStatisticsEnum stat)
    {
        return "GameStat_" stat.ToString();
    }

    public float GetStatValue(GameStatisticsEnum stat)
    {
        switch (stat) {
            case GameStatisticsEnum.TimesGameOpened:
                return TimesGameOpened;
            case GameStatisticsEnum.TimePlayed:
                return TimePlayed;
            case GameStatisticsEnum.TimeBattling:
                return TimeBattling;
            case GameStatisticsEnum.VersusBattleCount:
                return VersusBattleCount;
            case GameStatisticsEnum.VersusRoundCount:
                return VersusRoundCount;
            case GameStatisticsEnum.SpellCastCount:
                return SpellCastCount;
            case GameStatisticsEnum.DeathCount:
                return DeathCount;
            default:
                throw new ArgumentOutOfRangeException(nameof(stat), stat, null);
        }
    }

    public void SetStat(GameStatisticsEnum stat, float value)
    {
        switch (stat) {
            case GameStatisticsEnum.TimesGameOpened:
                TimesGameOpened = (int)value;
                break;
            case GameStatisticsEnum.TimePlayed:
                TimePlayed = value;
                break;
            case GameStatisticsEnum.TimeBattling:
                TimeBattling = value;
                break;
            case GameStatisticsEnum.VersusBattleCount:
                VersusBattleCount = (int)value;
                break;
            case GameStatisticsEnum.VersusRoundCount:
                VersusRoundCount = (int)value;
                break;
            case GameStatisticsEnum.SpellCastCount:
                SpellCastCount = (int)value;
                break;
            case GameStatisticsEnum.DeathCount:
                DeathCount = (int)value;
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(stat), stat, null);
        }
    }
}

With this approach I essentially use the enum as a Key to get the localization id and the value for a stat.

But it seems overly verbose. Esepcially if I end up have 50-70 stats to track.

The main goal here is to be able to serialize the stats so that they can be written to disk, and that they can easily be accessible from a key that can also be serialized (in this case I'm using an enum, but anything could do).

My main worry with enum is that I'll need to be careful not to reorder the elements in case I serialize the enum values for the UI. And that can be a problem if I add or remove stats (This is an iterative process)

I was thinking about using ScriptableObjects as keys... but I'm wondering if that's really a better approach. Otherwise I could use strings... but that' error prone... Unless I make a custom property drawer that gives me dropdown of predefined strings.

Any thoughts on this?

CodePudding user response:

Usually when you have enums this long it’s screaming at adding another level of abstraction.I think I’m this case you may be looking to leverage polymorphism through a technique called the ”strategy pattern”. There are a lot of resources you can Google. Hopefully those keywords get you started.

Basically you create an abstract class called Stat.

All stat subclasses can inherit their methods for serialization/deserialization from this base class. The stats themselves save/load themselves and can find the appropriate values themselves.

To use a stat, say death count. You can do something like this [pseudocode]

You load it:

 DeathCountStat deathCount = new DeathCountStat();
if File.Exists(deathcountsavefile):

 deathCount.Load(deathcountsavefile);
deathCountUI.DeathCount= deathCount.Value

To update

deathCount.Value  = 1;
deathCountUI.DeathCount= deathCount.Value;

To save

deathCount.Save(deathcountsavefile);

Because all stats inherit from the same base class your game can treat them and display them all in the same manner. For example you could have a window that contains 20 different stats of your choice, and you can just say

List<Stat> stats
stats.Add(deathCount);
stats.Add(killCount);
stats.Add(timePlayed);

Then 

Foreach(var stat in stats):
   DisplayStat(stat); //same method for all
  • Related