Trying to add a Leaderboard to the Tanks! Tutorial

Hi.

I’m trying to add a Leaderboard to the Tank! Tutorial… I’ve made so that each player has to enter a Player Name at the start of the game… and I want to store the total amount of rounds won in the game in a leaderboard.

Creating a Leaderboard using PlayerPrefs is quite easy… however, I would want to check if the player’s name is already on the leaderboard and if so - add the number of rounds won to that entry in the PlayerPrefs - then sorting the leaderboard before displaying it.

I have translated this one (How do I make a highscores board? - Questions & Answers - Unity Discussions) in to C#… can that script easily be modified to do what I want?

Sure, that post (which I skimmed) seems reasonable.
You can have a key with say John’s score “Score_John” and if it exists you can check if the new score is higher and replace it if it is. If it doesn’t exist, just add it.
A list of integers is sorted from low to high, by default, I believe. So , simply reverse that before you display it :slight_smile:

1 Like

So, I’ve been trying to do this for a while now… It finds the player in the leaderboard, it updates the score but duplicates the entry; so each time I run “AddScore”, ‘Johns’ score gets updated, and then duplicated so he takes both first and second place on the leaderboard…

I guess something’s wrong in my second for-loop?

Here’s my code:

using UnityEngine;
using Tankaton.Utilities;

public class Leaderboard : PersistentSingleton<Leaderboard> {

    [HideInInspector]
    public int[] leaderboardScore = new int[5];

    [HideInInspector]
    public string[] leaderboardName = new string[5];

    private void Start()
    {
        for(int i = 0; i < 5; i++)
        {
            if(PlayerPrefs.HasKey(i + "HScore"))
            {
                Debug.Log("#" + i + " " + PlayerPrefs.GetString(i + "HScoreName") + " >> " + PlayerPrefs.GetInt(i + "HScore"));
            }
        }
    }

    public void AddScore(string userName, int userScore)
    {
        int newScore;
        string newName;

        int oldScore;
        string oldName;

        newScore = userScore;
        newName = userName;

        for(int n = 0; n < 5; n++)
        {
            if(PlayerPrefs.GetString(n + "HScoreName") == userName)
            {
                Debug.Log("PLAYER FOUND ON LEADERBOARD (" + userName + ") AT POSITION " + n);
                int tmpScore = (userScore + PlayerPrefs.GetInt(n + "HScore"));
                Debug.Log("Score to add: " + userScore + " | New score: " + tmpScore);

                PlayerPrefs.SetString(n + "HScoreName", userName);
                PlayerPrefs.SetInt(n + "HScore", tmpScore);

                newScore = tmpScore;
                newName = userName;
                break;
            }
        }

        for(int i = 0; i < 5; i++)
        {
            if(PlayerPrefs.HasKey(i + "HScore"))
            {
                if(PlayerPrefs.GetInt(i + "HScore") < newScore)
                {
                    oldScore = PlayerPrefs.GetInt(i + "HScore");
                    oldName = PlayerPrefs.GetString(i + "HScoreName");

                    PlayerPrefs.SetInt(i + "HScore", newScore);
                    PlayerPrefs.SetString(i + "HScoreName", newName);

                    newScore = oldScore;
                    newName = oldName;
                }
            } else
            {
                PlayerPrefs.SetInt(i + "HScore", newScore);
                PlayerPrefs.SetString(i + "HScoreName", newName);
                newScore = 0;
                newName = "";
            }
        }

        PlayerPrefs.Save();
    }

}

When you’re saving, and if John’s score already exists… you have to go over the list, find out where his new score is higher in the list, put it at that spot, and keep going down the list:
On your way down, you push the name John beat out downwards, and when you encounter his name again, you overwrite it.
Sally 4
Jim 3
Bob 3
John 2
Tracy 2

Into John = 4
Sally 4
John 4
Jim 3
Bob 3
Tracy 2

So, we saw John was there, and his new score is 4
So we check, … and he’s not higher than sally, so just skip (leave it)
He’s higher than Jim, so we save Jim’s name, write John’s (+ his score),
now we temporarily save Bob and his score and write Jim’s in his place
etc… :slight_smile: We Find the original John, and overwrite that with our temp from Bob.
That make sense?

Makes total sense :slight_smile: But my loops are totally screwed up haha… I really can’t get this to work… I just deleted everything and I will start from scratch… :slight_smile:

I sympathize… If you write back with some new code that attempts to follow that idea, and it’s not working I can try to look at it, again in the new format. I had several other ideas on how this could have been done while writing that, but that just seemed like an easy way to explain the idea :slight_smile:

Here’s something I wrote a while ago which should do what you are after;

using System.Collections.Generic;
using UnityEngine;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;


public class Leaderboard : MonoBehaviour
{
    public List<HighScores> _highScores = new List<HighScores>();

    void Start()
    {
        Load();
    } 

    public void AddScore(string name, int score)
    {
        //Check if name is already stored
        for (int i = 0; i < _highScores.Count; i++)
        {
            if (_highScores[i].Name == name)
            {
                if (score > _highScores[i].Score)
                    _highScores[i].Score = score;

                SortScores();
                return;
            }
        }
        //Add new name and score if it doesn't exist
        HighScores hScore = new HighScores()
        {
            Name = name,
            Score = score
        };
        _highScores.Add(hScore);
        SortScores();
    }

    private void SortScores()
    {
        if (_highScores.Count == 0)
            return;

        _highScores.Sort(delegate (HighScores x, HighScores y)
        {
            return x.Score.CompareTo(y.Score);
        });

        _highScores.Reverse();
    }

    public void Save()
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Create(Application.persistentDataPath + "/HighScores.bin");

        SavedScores hs = new SavedScores()
        {
            highscores = _highScores
        };
        bf.Serialize(file, hs);
        file.Close();
    }

    public void Load()
    {
        if (File.Exists(Application.persistentDataPath + "/HighScores.bin"))
        {
            try
            {
                BinaryFormatter bf = new BinaryFormatter();
                FileStream file = File.Open(Application.persistentDataPath + "/HighScores.bin", FileMode.Open);
                SavedScores hs = (SavedScores)bf.Deserialize(file);
                file.Close();
                _highScores = hs.highscores;
            }
            catch
            {
                //Ignore
            }
        }
    }
}

[System.Serializable]
public class SavedScores
{
    public List<HighScores> highscores = new List<HighScores>();
}

[System.Serializable]
public class HighScores
{
    public string Name;
    public int Score;
}

Thank you! That seem to work quite nice… Except for one thing I’m trying to fix. The max score each player can get i 5 (one for each round won), I want to Add that number to the player’s current score with this code:

// Check if name is already stored
        for(int i = 0; i < _highscores.Count; i++)
        {
            if(_highscores[i].Name == name)
            {
                _highscores[i].Score = (_highscores[i].Score + score);
                SortScores();
                return;
            }
        }

However, the score always is 5, nothing is never added… (?)

EDIT: Nevermind, solved it… forgot to Save() before return; was called…