Complete GameStateManager.cs

 Here's the complete, ready-to-use code for managing game states with singleton in Unity:

using System;
using UnityEngine;

public class GameStateManager : MonoBehaviour
{
    // Singleton instance
    private static GameStateManager _instance;
    
    public static GameStateManager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = FindObjectOfType<GameStateManager>();
                
                if (_instance == null)
                {
                    GameObject go = new GameObject("GameStateManager");
                    _instance = go.AddComponent<GameStateManager>();
                }
            }
            return _instance;
        }
    }
    
    // Define all possible game states
    public enum GameState
    {
        MainMenu,
        Playing,
        Paused,
        GameOver,
        Victory
    }
    
    // Event that fires when state changes
    public event Action<GameState, GameState> OnStateChanged;
    
    // Current state storage
    private GameState _currentState;
    
    public GameState CurrentState
    {
        get { return _currentState; }
        private set
        {
            if (_currentState != value)
            {
                GameState previousState = _currentState;
                _currentState = value;
                HandleStateChange(previousState, _currentState);
                OnStateChanged?.Invoke(previousState, _currentState);
            }
        }
    }
    
    void Awake()
    {
        // Enforce singleton pattern
        if (_instance != null && _instance != this)
        {
            Destroy(gameObject);
            return;
        }
        
        _instance = this;
        DontDestroyOnLoad(gameObject);
        
        // Set initial state
        CurrentState = GameState.MainMenu;
    }
    
    // Public method to change state
    public void ChangeState(GameState newState)
    {
        CurrentState = newState;
    }
    
    // Handle what happens when state changes
    private void HandleStateChange(GameState previous, GameState current)
    {
        Debug.Log($"Game State changed from {previous} to {current}");
        
        switch (current)
        {
            case GameState.MainMenu:
                Time.timeScale = 1f;
                Cursor.visible = true;
                Cursor.lockState = CursorLockMode.None;
                break;
                
            case GameState.Playing:
                Time.timeScale = 1f;
                Cursor.visible = false;
                Cursor.lockState = CursorLockMode.Locked;
                break;
                
            case GameState.Paused:
                Time.timeScale = 0f;
                Cursor.visible = true;
                Cursor.lockState = CursorLockMode.None;
                break;
                
            case GameState.GameOver:
                Time.timeScale = 0f;
                Cursor.visible = true;
                Cursor.lockState = CursorLockMode.None;
                break;
                
            case GameState.Victory:
                Time.timeScale = 0f;
                Cursor.visible = true;
                Cursor.lockState = CursorLockMode.None;
                break;
        }
    }
    
    // Helper methods for common state checks
    public bool IsPlaying()
    {
        return CurrentState == GameState.Playing;
    }
    
    public bool IsPaused()
    {
        return CurrentState == GameState.Paused;
    }
    
    public bool IsGameOver()
    {
        return CurrentState == GameState.GameOver;
    }
}

Example Usage Scripts

PlayerController.cs

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float moveSpeed = 5f;
    public int health = 100;
    
    void Update()
    {
        // Only allow movement when playing
        if (GameStateManager.Instance.IsPlaying())
        {
            HandleMovement();
        }
        
        // Pause/Unpause with Escape key
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            if (GameStateManager.Instance.IsPlaying())
            {
                GameStateManager.Instance.ChangeState(GameStateManager.GameState.Paused);
            }
            else if (GameStateManager.Instance.IsPaused())
            {
                GameStateManager.Instance.ChangeState(GameStateManager.GameState.Playing);
            }
        }
    }
    
    void HandleMovement()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        
        Vector3 movement = new Vector3(horizontal, 0, vertical);
        transform.Translate(movement * moveSpeed * Time.deltaTime);
    }
    
    public void TakeDamage(int damage)
    {
        health -= damage;
        
        if (health <= 0)
        {
            Die();
        }
    }
    
    void Die()
    {
        Debug.Log("Player died!");
        GameStateManager.Instance.ChangeState(GameStateManager.GameState.GameOver);
    }
}

UIManager.cs

using UnityEngine;
using UnityEngine.UI;

public class UIManager : MonoBehaviour
{
    [Header("UI Panels")]
    public GameObject mainMenuPanel;
    public GameObject pausePanel;
    public GameObject gameOverPanel;
    public GameObject victoryPanel;
    public GameObject hudPanel;
    
    [Header("Buttons")]
    public Button playButton;
    public Button resumeButton;
    public Button restartButton;
    public Button quitButton;
    
    void Start()
    {
        // Subscribe to state changes
        GameStateManager.Instance.OnStateChanged += HandleStateChange;
        
        // Setup button listeners
        if (playButton != null)
            playButton.onClick.AddListener(OnPlayButtonClicked);
        
        if (resumeButton != null)
            resumeButton.onClick.AddListener(OnResumeButtonClicked);
        
        if (restartButton != null)
            restartButton.onClick.AddListener(OnRestartButtonClicked);
        
        if (quitButton != null)
            quitButton.onClick.AddListener(OnQuitButtonClicked);
        
        // Initialize UI based on current state
        UpdateUI(GameStateManager.Instance.CurrentState);
    }
    
    void OnDestroy()
    {
        // Unsubscribe from events
        if (GameStateManager.Instance != null)
        {
            GameStateManager.Instance.OnStateChanged -= HandleStateChange;
        }
    }
    
    private void HandleStateChange(GameStateManager.GameState previous, GameStateManager.GameState current)
    {
        UpdateUI(current);
    }
    
    private void UpdateUI(GameStateManager.GameState state)
    {
        // Hide all panels first
        if (mainMenuPanel != null) mainMenuPanel.SetActive(false);
        if (pausePanel != null) pausePanel.SetActive(false);
        if (gameOverPanel != null) gameOverPanel.SetActive(false);
        if (victoryPanel != null) victoryPanel.SetActive(false);
        if (hudPanel != null) hudPanel.SetActive(false);
        
        // Show appropriate panel
        switch (state)
        {
            case GameStateManager.GameState.MainMenu:
                if (mainMenuPanel != null) mainMenuPanel.SetActive(true);
                break;
                
            case GameStateManager.GameState.Playing:
                if (hudPanel != null) hudPanel.SetActive(true);
                break;
                
            case GameStateManager.GameState.Paused:
                if (pausePanel != null) pausePanel.SetActive(true);
                if (hudPanel != null) hudPanel.SetActive(true);
                break;
                
            case GameStateManager.GameState.GameOver:
                if (gameOverPanel != null) gameOverPanel.SetActive(true);
                break;
                
            case GameStateManager.GameState.Victory:
                if (victoryPanel != null) victoryPanel.SetActive(true);
                break;
        }
    }
    
    // Button callbacks
    private void OnPlayButtonClicked()
    {
        GameStateManager.Instance.ChangeState(GameStateManager.GameState.Playing);
    }
    
    private void OnResumeButtonClicked()
    {
        GameStateManager.Instance.ChangeState(GameStateManager.GameState.Playing);
    }
    
    private void OnRestartButtonClicked()
    {
        UnityEngine.SceneManagement.SceneManager.LoadScene(
            UnityEngine.SceneManagement.SceneManager.GetActiveScene().name
        );
        GameStateManager.Instance.ChangeState(GameStateManager.GameState.Playing);
    }
    
    private void OnQuitButtonClicked()
    {
        #if UNITY_EDITOR
            UnityEditor.EditorApplication.isPlaying = false;
        #else
            Application.Quit();
        #endif
    }
}

EnemyController.cs

using UnityEngine;

public class EnemyController : MonoBehaviour
{
    public float moveSpeed = 3f;
    public int damage = 10;
    
    private Transform player;
    
    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player")?.transform;
    }
    
    void Update()
    {
        // Only move when game is playing
        if (GameStateManager.Instance.IsPlaying() && player != null)
        {
            ChasePlayer();
        }
    }
    
    void ChasePlayer()
    {
        Vector3 direction = (player.position - transform.position).normalized;
        transform.position += direction * moveSpeed * Time.deltaTime;
    }
    
    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Player"))
        {
            PlayerController playerController = collision.gameObject.GetComponent<PlayerController>();
            if (playerController != null)
            {
                playerController.TakeDamage(damage);
            }
        }
    }
}

GameWinTrigger.cs

using UnityEngine;

public class GameWinTrigger : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            Debug.Log("Player reached the goal!");
            GameStateManager.Instance.ChangeState(GameStateManager.GameState.Victory);
        }
    }
}

Setup Instructions

  1. Create an empty GameObject in your scene and name it "GameStateManager"
  2. Attach the GameStateManager.cs script to it
  3. Create UI Canvas with panels for MainMenu, Pause, GameOver, Victory, and HUD
  4. Create another empty GameObject and attach UIManager.cs
  5. Assign all UI panels and buttons in the UIManager inspector
  6. Attach PlayerController.cs to your player GameObject
  7. Tag your player as "Player" in the inspector
  8. Attach EnemyController.cs to enemy GameObjects
  9. Create a goal trigger with GameWinTrigger.cs attached

Now your game will properly manage states throughout gameplay!

Comments