'When and where should I be triggering my high score entry?

I am tackling a Unity course challenge, where we are supposed to implement data persistence between scenes and across sessions. After deciding to go with JSON, I managed to figure out how to save data generally between game sessions and scenes (in terms of preserving a game load or displaying the last played session playerName and playerScore). However, the high score table I'm using is puzzling me. When and where should I be triggering my high score entry, given that the high score table is in a separate scene with its own separate savefile path?

So far, I have a MainManager script that holds basic player data, which helps communicate between the MainMenuController script and the GameController script, located in the Main scene and Game scene respectively. The MainManager script has JSON save file functionality, while the HighScoreTable script (in the HiScore scene) has its own save function and filepath (currently using PlayerPrefs, which I'm going to change to JSON later). The script is based off CodeMonkey's script.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class HighscoreTable : MonoBehaviour
{
    private Transform entryContainer;
    private Transform entryTemplate;
    private List<Transform> highscoreEntryTransformList;

    public string lastPlayerName;
    public float lastPlayerScore;

    private void Awake()
    {
        entryContainer = transform.Find("highscoreEntryContainer");
        entryTemplate = entryContainer.Find("highscoreEntryTemplate");

        entryTemplate.gameObject.SetActive(false);

        string jsonString = PlayerPrefs.GetString("highscoreTable");
        Highscores highscores = JsonUtility.FromJson<Highscores>(jsonString);

        //if there is no stored table, initialize
        if(highscores == null)
        {
            Debug.Log("Initializing table with default values...");
            AddHighscoreEntry(1234, "KAL");
            AddHighscoreEntry(345, "TAM");
            AddHighscoreEntry(897621, "JOE");

            // Reload
            jsonString = PlayerPrefs.GetString("highscoreTable");
            highscores = JsonUtility.FromJson<Highscores>(jsonString);
        }

        //for sorting without save / load(otherwise we can sort on a load function
        //for (int i = 0; i < highscores.highscoreEntryList.Count; i++)
        //{
        //    for (int j = i + 1; j < highscores.highscoreEntryList.Count; j++)
        //    {
        //        if (highscores.highscoreEntryList[j].score > highscores.highscoreEntryList[i].score)
        //        {
        //            //swap

        //            HighscoreEntry tmp = highscores.highscoreEntryList[i];
        //            highscores.highscoreEntryList[i] = highscores.highscoreEntryList[j];
        //            highscores.highscoreEntryList[j] = tmp;
        //        }
        //    }
        //}
        highscores.highscoreEntryList.Sort((x, y) => y.score.CompareTo(x.score));

        //keep max count and delete extra
        if (highscores.highscoreEntryList.Count > 10)
        {
            for (int h = highscores.highscoreEntryList.Count; h > 10; h--)
            {
                highscores.highscoreEntryList.RemoveAt(10);
            }
        }

        highscoreEntryTransformList = new List<Transform>();
        foreach (HighscoreEntry highscoreEntry in highscores.highscoreEntryList)
        {
            CreateHIghscoreEntryTransform(highscoreEntry, entryContainer, highscoreEntryTransformList);            
        }
    }

    private void Update()
    {
        
    }

    private void CreateHIghscoreEntryTransform(HighscoreEntry highscoreEntry, Transform container, List<Transform> transformList)
    {
        float templateHeight = 30f;

        Transform entryTransform = Instantiate(entryTemplate, container);
        RectTransform entryRectTransform = entryTransform.GetComponent<RectTransform>();
        entryRectTransform.anchoredPosition = new Vector2(0, -templateHeight * transformList.Count);
        entryTransform.gameObject.SetActive(true);

        int rank = transformList.Count + 1;
        string rankString;
        switch (rank)
        {
            default:
                rankString = rank + "TH"; break;

            case 1: rankString = "1ST"; break;
            case 2: rankString = "2ND"; break;
            case 3: rankString = "3RD"; break;
        }

        entryTransform.Find("posText").GetComponent<TMP_Text>().text = rankString;

        string name = highscoreEntry.name;
        entryTransform.Find("nameText").GetComponent<TMP_Text>().text = name;        

        float score = highscoreEntry.score;
        entryTransform.Find("scoreText").GetComponent<TMP_Text>().text = score.ToString();

        transformList.Add(entryTransform);
    }

    public void AddHighscoreEntry(float score, string name)
    {
        //create highscore entry
        HighscoreEntry highscoreEntry = new HighscoreEntry { score = score, name = name };

        //load saved highscores
        string jsonString = PlayerPrefs.GetString("highscoreTable");
        Highscores highscores = JsonUtility.FromJson<Highscores>(jsonString);

        //if there's no stored table, initialize
        if (highscores == null)
        {
            highscores = new Highscores()
            {
                highscoreEntryList = new List<HighscoreEntry>()
            };
        }

        //add new entry to list
        highscores.highscoreEntryList.Add(highscoreEntry);

        //keep max count and delete extra
        if (highscores.highscoreEntryList.Count > 10)
        {
            for (int h = highscores.highscoreEntryList.Count; h > 10; h--)
            {
                highscores.highscoreEntryList.RemoveAt(10);
            }
        }

        //save updated highscores
        string json = JsonUtility.ToJson(highscores);
        PlayerPrefs.SetString("highscoreTable", json);
        PlayerPrefs.Save();
    }

    private class Highscores
    {
        public List<HighscoreEntry> highscoreEntryList;
    }

    //represents a single highscore entry

    [System.Serializable]
    private class HighscoreEntry
    {
        public float score;
        public string name;
    }
}

Everything APPEARS to be working (so far). The highscore table doesn't initialize, displaying the default names and values provided in the code. However, I am struggling to figure out where/when to implement the "AddHighscoreEntry" method. Here is what I tried out:

  • When the player dies.
  • When the Game Over screen pops up.
  • When the High Score scene button is clicked on the Main Menu.
  • In the HighscoreTable update function, tracking the player's health through MainManager and creating a new entry when the player dies.

In some cases, the update function causes the entry to replicate infinitely, filling out the high score table. In other cases, it stops surrounding code entirely, bringing the game to a grinding halt. Sometimes it triggers the table to fill out the positions and a line of 0s (but no player names). I can clear those by deleting PlayerPrefs with a button I made... but I can't seem to instantiate a single entry with all the details.

Is the fact that I'm currently using two save files the root of the problem? And if I were to switch the file to JSON, I still have the problem of when/where to trigger the AddHighscoreEntry. Where would you recommend I put it? My Github repo of spaghetti code is here: https://github.com/scarecrowslady/JProgramming_DataPersistenceProject_R2.git



Solution 1:[1]

Maybe it's happening because of you trying to interact with other classes/objects at Awake. Awake should be used for initalizing objects without interact other classes. You should better use Start for intecract with other classes/objects.

In my opinion, you should practice SOLID, Design Patterns. In this case you could use Observer pattern here.

Order of execution for event functions

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 oistikbal