Part of these game systems:
beginner Touch & Mobile

Score Manager

Complete score management system with high score persistence, combo multipliers with decay, and score change events. Uses singleton pattern for easy global access.

Unity 2022.3+ · 3.3 KB · ScoreManager.cs

How to Use

1

Create an empty GameObject named ScoreManager in your scene

2

Attach the ScoreManager script to it

3

Set comboDecayTime and maxMultiplier in the inspector

4

Call ScoreManager.Instance.AddScore(points) from gameplay scripts

5

Connect onScoreChanged and onHighScoreBeaten events to your UI text elements

Source Code

ScoreManager.cs
C#
using UnityEngine;
using UnityEngine.Events;

public class ScoreManager : MonoBehaviour
{
    public static ScoreManager Instance { get; private set; }

    [Header("Combo Settings")]
    [SerializeField] private float comboDecayTime = 3f;
    [SerializeField] private int maxMultiplier = 10;

    [Header("High Score")]
    [SerializeField] private string highScoreKey = "HighScore";

    [Header("Events")]
    public UnityEvent<int> onScoreChanged;
    public UnityEvent<int> onHighScoreBeaten;
    public UnityEvent<int> onComboChanged;

    public int CurrentScore { get; private set; }
    public int HighScore { get; private set; }
    public int ComboMultiplier { get; private set; } = 1;
    public int ComboCount { get; private set; }
    public float ComboTimer { get; private set; }

    private float comboTimerMax;
    private bool highScoreBeaten;

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);

        HighScore = PlayerPrefs.GetInt(highScoreKey, 0);
        comboTimerMax = comboDecayTime;
    }

    private void Update()
    {
        if (ComboCount > 0)
        {
            ComboTimer -= Time.deltaTime;
            if (ComboTimer <= 0f)
            {
                ResetCombo();
            }
        }
    }

    public void AddScore(int points)
    {
        int earnedPoints = points * ComboMultiplier;
        CurrentScore += earnedPoints;

        // Combo
        ComboCount++;
        ComboTimer = comboTimerMax;
        ComboMultiplier = Mathf.Min(1 + ComboCount / 5, maxMultiplier);
        onComboChanged?.Invoke(ComboMultiplier);

        onScoreChanged?.Invoke(CurrentScore);

        // High score check
        if (CurrentScore > HighScore)
        {
            HighScore = CurrentScore;
            SaveHighScore();

            if (!highScoreBeaten)
            {
                highScoreBeaten = true;
                onHighScoreBeaten?.Invoke(HighScore);
            }
        }
    }

    public int GetScore()
    {
        return CurrentScore;
    }

    public int GetHighScore()
    {
        return HighScore;
    }

    public void ResetScore()
    {
        CurrentScore = 0;
        highScoreBeaten = false;
        ResetCombo();
        onScoreChanged?.Invoke(CurrentScore);
    }

    public void ResetCombo()
    {
        ComboCount = 0;
        ComboMultiplier = 1;
        ComboTimer = 0f;
        onComboChanged?.Invoke(ComboMultiplier);
    }

    public void SetComboDecayTime(float time)
    {
        comboDecayTime = Mathf.Max(0.5f, time);
        comboTimerMax = comboDecayTime;
    }

    public float GetComboTimerNormalized()
    {
        if (comboTimerMax <= 0f) return 0f;
        return Mathf.Clamp01(ComboTimer / comboTimerMax);
    }

    private void SaveHighScore()
    {
        PlayerPrefs.SetInt(highScoreKey, HighScore);
        PlayerPrefs.Save();
    }

    public void ClearHighScore()
    {
        HighScore = 0;
        PlayerPrefs.DeleteKey(highScoreKey);
        PlayerPrefs.Save();
    }

    public string GetFormattedScore()
    {
        return FormatNumber(CurrentScore);
    }

    public string GetFormattedHighScore()
    {
        return FormatNumber(HighScore);
    }

    public static string FormatNumber(int number)
    {
        if (number >= 1000000) return (number / 1000000f).ToString("F1") + "M";
        if (number >= 1000) return (number / 1000f).ToString("F1") + "K";
        return number.ToString();
    }

    private void OnDestroy()
    {
        if (Instance == this)
            Instance = null;
    }
}
Ready for more? Idle Income Generator Passive income system for idle/clicker games with offline earnings calculation, upgrade scaling, and prestige mechanics.