Part of these game systems:
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.
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
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;
}
}