how to transfer score and time to a highscore table

so i have objects that you need to match correctly(dragging a card to the correct dropzone), once matched, i have a counter that adds the total of correct and incorrect card matches, plus there is a timer that tracks how long it takes you to match all the objects(matching mean dropping the card in the correct dropzone).

once all the items match, the timer stops, so the ingame scoreboard will show the Total correct, incorrect matches, plus the total time it took to complete.

now i want to take that data and put it on a highscore table, that shows the following: Rank(this captures 1st, 2nd, 3d, etc), Name, Incorrect matches, and Time. the rank will be based on the incorrect matches (the lower the incorrect number the higher you are on the highscore table). The next would be the time, so if two people had 0 incorrect, the one with the lower time would be higher.

can someone help me figure this out, i was trying to use playerprefs, but didnt work.

the script for time and the correct/incorrect match is below:


   
 

         public class Timer : ScoreTextScript
           { 
               public Text timerText;
               private float startTime;
               private bool timerisActive = true;
               public GameObject HighScore;  //HighScore table for game scores
           
               // Start is called before the first frame update
               void Start()
               {
                   startTime = Time.time;
                   
               }
           
               // Update is called once per frame
               void Update()
               {if (timerisActive == true)
                   {
                       float t = Time.time - startTime;
           
                       string minutes = ((int)t / 60).ToString();
                       string seconds = (t % 60).ToString("f2");
           
                       timerText.text = minutes + ":" + seconds;
                   }
                   if (correctAmount>= 100)
                   {
                       timerisActive = false;
                       HighScore.gameObject.SetActive(true);
           

Alright so I don’t know what you tried exactly in Playerprefs however you can also simply serialize it and save it yourself. Maybe make a class like so:

  [System.Serializable]
    public class HighScore
    {
        public string Name;
        public float Time;
        public int Incorrect;
    }

Then after each game create a new instance of that object, fill out the properties add it to the List serialize it and save it on the harddrive. Then whenever people open the highscore board, you deserialize the list of HighScores and order them the way you like.

Ok, this is what I came up with:

using System.Linq;
using System;
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public class HighScoreBoard : MonoBehaviour     //Placed on the ScoreBoard Panel
{
    HighScoreBoardSerializer board = new HighScoreBoardSerializer();
    public SaveHighScores highScores;   //Drag in EventSystem into inspector
    Transform scores;

    public void StartGame()     //Called when starting a new game, loads saved data if available, otherwise creates the following
    {
        scores = transform.GetChild(1);     //Set the second child, Scores Empty Object, to scores
        board = highScores.LoadGameData();
        if(board.HighScoreList.Count == 0)  //New Game, an example of how to add scores to the High Score List
        {
            board.HighScoreList.Add(new HighScore { 
                Name = "Player01", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player02", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player03", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player04", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player05", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player06", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player07", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player08", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player09", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
            board.HighScoreList.Add(new HighScore { 
                Name = "Player10", Time = 0.0f, Incorrect = 0, dateTime = DateTime.Now.ToString() });
        }
    }

    public void SaveGame()  //Called on completion of a game
    {
        highScores.SaveGameData(board);
    }

    public void DisplayScoreBoard()    //Does what it says
    {
        board.HighScoreList = board.HighScoreList.OrderBy
        (
            i => i.Incorrect    //First sort by Incorrect guesses
        ).ThenBy
        (
            t => t.Time     //Second, sort by Time elapsed
        ).ThenBy
        (
            p => p.dateTime     //Finally, sort by Date and Time completed
        ).ToList();

        gameObject.SetActive(true);

        int count = 0;
        foreach (HighScore score in board.HighScoreList)
        {
            scores.GetChild(count).GetComponent<Text>().text = 
                (count + 1) + ": " + score.Name + " - " + score.Time + " - " + score.Incorrect + " - " + score.dateTime;
            count++;
            if(count > 9)
            {
                break;  //Only display the top 10 scores
            }
        }
    }

    public void CloseScoreBoard()   //Close Button on the ScoreBoard
    {
        gameObject.SetActive(false);
    }
}

[Serializable]
public class HighScoreBoardSerializer
{
    public List<HighScore> HighScoreList = new List<HighScore>();
}

[Serializable]
public class HighScore
{
    public string Name;
    public float Time;
    public int Incorrect;
    public string dateTime;  //Added as a third check in case more than 1 player shares Incorrect and Time
}

A separate script for saving the data to file:

using UnityEngine;
using System.IO;

public class SaveHighScores : MonoBehaviour     //Placed on the EventSystem
{
    string GetSaveLocationPath()    //Create a Path variable so we don't need to keep getting the path
    {
        string folder = Application.persistentDataPath;
        string file = "Save.json";
        string fullPath = Path.Combine(folder, file);
        return fullPath;
    }

    public void SaveGameData(HighScoreBoardSerializer board)
    {
        string json = JsonUtility.ToJson(board);    //Format data to json
        string fullPath = GetSaveLocationPath();

        if(File.Exists(fullPath))
        {
            Debug.Log("File Deleted");
            File.Delete(fullPath);      //Delete the file instead of adding to it
        }

        File.WriteAllText(fullPath, json);
    }

    public HighScoreBoardSerializer LoadGameData()
    {
        string fullPath = GetSaveLocationPath();
        if(File.Exists(fullPath))
        {
            string jsonString = File.ReadAllText(fullPath);
            Debug.Log("Game Loaded");
            return JsonUtility.FromJson<HighScoreBoardSerializer>(jsonString);
        }
        else
        {
            Debug.Log("New Game");
            return new HighScoreBoardSerializer();
        }
    }
}

and an example of the layout, this you will need to tweak to your needs but it shows how the Hierarchy was set up:

I’m sure there are more elegant ways of doing it but this works, feel free to ask about anything you may not understand.

Sorry, got stuck working on my car. I’m not sure why it would give you errors. If I copy and paste the scripts from here everything works fine. The Serialized classes should be able to remain outside of the main class. What errors does it give you?

Here we will go on from here for now.