unity score system mobile game UI

How to Build a Score and High Score System in Unity for Mobile Games

Small Game Mechanics & Prototyping

Almost every mobile game has a score. Whether it’s points earned from collecting coins, enemies defeated, or distance traveled, the score is usually the primary feedback loop that keeps players engaged. Tracking a high score across sessions — so the player has something to beat each time they open the game — adds a layer of replayability that costs almost nothing to implement. In this tutorial you’ll build a complete score and high score system in Unity from scratch: a live score counter that updates during play, a high score that persists between sessions using PlayerPrefs, and a UI that displays both clearly on screen.

What You’ll Build

By the end of this tutorial you’ll have a ScoreManager script that handles all score logic in one place, two TextMeshPro UI elements showing current score and high score, and a system that automatically saves and loads the high score between sessions. The score can be incremented from any other script in your game — a coin pickup, an enemy kill, a distance counter — by calling a single method.

This tutorial assumes you have a Unity project set up for mobile (Android or iOS), basic familiarity with creating GameObjects and attaching scripts, and TextMeshPro installed (it comes with Unity by default — go to Window → Package Manager and confirm it’s installed if you’re unsure).

Step 1: Set Up the UI

First, create the score display in your scene. In the Hierarchy, right-click and go to UI → Canvas. If you already have a Canvas, you can use it. Inside the Canvas, right-click and go to UI → Text – TextMeshPro to create two text elements.

Name the first one ScoreText and the second one HighScoreText. Position ScoreText in the top center of the screen and HighScoreText just below it, or in a corner — wherever suits your game’s layout. Set their initial text to “Score: 0” and “Best: 0” respectively. Adjust font size and color as needed for readability on mobile screens.

In the Canvas component, set the Render Mode to Screen Space – Overlay and make sure the Canvas Scaler’s UI Scale Mode is set to Scale With Screen Size, with a Reference Resolution of 1080×1920 (portrait) or 1920×1080 (landscape). This ensures the UI scales correctly across different device screen sizes.

Step 2: Create the ScoreManager Script

Create a new C# script called ScoreManager. In your Project window, right-click in the Scripts folder (or create one), go to Create → C# Script, and name it ScoreManager. Open it and replace the default content with the following:

using UnityEngine;
using TMPro;

public class ScoreManager : MonoBehaviour
{
    public static ScoreManager Instance;

    [Header("UI References")]
    public TMP_Text scoreText;
    public TMP_Text highScoreText;

    private int currentScore = 0;
    private int highScore = 0;

    private const string HIGH_SCORE_KEY = "HighScore";

    void Awake()
    {
        // Singleton pattern — only one ScoreManager exists at a time
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Destroy(gameObject);
        }
    }

    void Start()
    {
        // Load the saved high score when the game starts
        highScore = PlayerPrefs.GetInt(HIGH_SCORE_KEY, 0);
        UpdateUI();
    }

    public void AddScore(int amount)
    {
        currentScore += amount;

        // Check if the current score beats the high score
        if (currentScore > highScore)
        {
            highScore = currentScore;
            PlayerPrefs.SetInt(HIGH_SCORE_KEY, highScore);
            PlayerPrefs.Save();
        }

        UpdateUI();
    }

    public void ResetScore()
    {
        currentScore = 0;
        UpdateUI();
    }

    public int GetCurrentScore()
    {
        return currentScore;
    }

    public int GetHighScore()
    {
        return highScore;
    }

    private void UpdateUI()
    {
        if (scoreText != null)
            scoreText.text = "Score: " + currentScore;

        if (highScoreText != null)
            highScoreText.text = "Best: " + highScore;
    }
}

Step 3: Attach the Script and Connect the UI

Create an empty GameObject in the Hierarchy — right-click, go to Create Empty, and name it ScoreManager. Drag the ScoreManager script onto this GameObject. In the Inspector, you’ll see two public fields: Score Text and High Score Text. Drag the ScoreText TextMeshPro object from your Canvas into the Score Text field, and HighScoreText into the High Score Text field.

Press Play. You should see “Score: 0” and “Best: 0” displayed on screen. The high score will persist between sessions automatically — PlayerPrefs saves data to the device’s local storage, so it survives the game being closed and reopened.

Step 4: Call AddScore From Other Scripts

The ScoreManager uses a singleton pattern, which means any script in your game can access it without needing a direct reference. To add points from a coin pickup, an enemy defeat, or any other event, call this from any script:

// Add 10 points — call this from any script, anywhere in the scene
ScoreManager.Instance.AddScore(10);

For example, in a coin pickup script using OnTriggerEnter2D:

void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("Player"))
    {
        ScoreManager.Instance.AddScore(10);
        Destroy(gameObject); // Remove the coin from the scene
    }
}

And to reset the score when a new game starts — for example, from a Game Over screen or a restart button:

ScoreManager.Instance.ResetScore();

The high score is not reset when ResetScore() is called — it only updates when the current score exceeds it during play. This means the player’s personal best persists correctly across multiple play sessions without any extra code.

Step 5: Display the Score on the Game Over Screen

Most mobile games show the final score and high score on a Game Over screen between rounds. If your game has a separate Game Over UI panel, add a TextMeshPro element to it for the final score and wire it up like this in your Game Over script:

using UnityEngine;
using TMPro;

public class GameOverScreen : MonoBehaviour
{
    public TMP_Text finalScoreText;
    public TMP_Text finalHighScoreText;

    public void ShowGameOver()
    {
        gameObject.SetActive(true);
        finalScoreText.text = "Score: " + ScoreManager.Instance.GetCurrentScore();
        finalHighScoreText.text = "Best: " + ScoreManager.Instance.GetHighScore();
    }
}

Call gameOverScreen.ShowGameOver() from wherever your game over logic triggers — after the player runs out of health, misses a target, or time runs out. The ScoreManager has already saved the high score to PlayerPrefs at this point, so no additional save call is needed.

Common Issues and How to Fix Them

Score text is not updating. The most common cause is that the scoreText or highScoreText references in the Inspector are not assigned. Check the ScoreManager GameObject in the Inspector and make sure both fields are connected to your TextMeshPro objects — they should show the object name, not “None (TMP Text)”.

High score resets every time the game runs. This happens when PlayerPrefs data is being cleared somewhere in your code, or if you’re testing in the Unity Editor and manually clearing PlayerPrefs via Edit → Clear All PlayerPrefs. On a real device this won’t happen — but check your code for any calls to PlayerPrefs.DeleteAll() that might be wiping saved data unintentionally.

NullReferenceException on ScoreManager.Instance. This error appears when another script tries to call ScoreManager.Instance before the ScoreManager GameObject exists in the scene. Make sure the ScoreManager is present in every scene where score tracking is needed, or use Unity’s DontDestroyOnLoad if you want it to persist across scene loads:

void Awake()
{
    if (Instance == null)
    {
        Instance = this;
        DontDestroyOnLoad(gameObject); // Persist across scene changes
    }
    else
    {
        Destroy(gameObject);
    }
}

With that, you have a complete, reusable score system. AddScore handles the arithmetic and the high score comparison. PlayerPrefs handles persistence automatically. The singleton pattern means any script can add points without needing a direct reference. This is the foundation that most casual mobile games build on — from here you can extend it with score multipliers, combo bonuses, or animated score popups as your game grows.

Leave a Reply

Your email address will not be published. Required fields are marked *