【Unity】Unity Game Architecture: Understanding Game Loop and Game Manager Pattern

Created: 2025-12-07

How do you manage overall game flow? Learn to implement the 'Game Manager' pattern for controlling game states (title, playing, game over) and centralizing critical data like scores and player status.

Overview

Moving beyond simple prototypes to full games with multiple scenes and complex rules, you'll face a design question: "Where and how should I manage overall game state?" For example:

  • Is the current game state "title screen," "playing," "paused," or "game over"?
  • Where should persistent data like player score, lives, and currency be stored across scenes?
  • Who controls overall game flow—starting, restarting, transitioning to the next level?

Scattering this "game-wide information and logic" across individual player and enemy scripts quickly produces "spaghetti code" that's hard to maintain and understand.

The classic solution is the Game Manager pattern. A Game Manager is an object that centrally manages game-wide state and logic—essentially a "command center."

Game Manager Responsibilities

What a Game Manager handles varies by game size and type, but typically includes:

  1. Game State Management: Holds game states defined with enum (e.g., Title, Playing, Paused, GameOver) and controls state transitions. Depending on current state, it can disable player input or show/hide UI.

  2. Game Rule Management: Handles score calculation, countdown timers, win/loss condition evaluation—the core game rules.

  3. Global Data Management: Stores data that multiple objects or scenes need to access—player score, lives, experience points.

  4. Providing References to Other Objects: Holds references to important manager classes like Player, UIManager, and AudioManager, providing a convenient access point for other objects.

Singleton Pattern Implementation

A Game Manager must be guaranteed to exist as exactly one instance in the game. The Singleton design pattern ensures this "single instance" and provides easy global access.

Here's a basic Game Manager implementation using the Singleton pattern:

using UnityEngine;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    // Static field holding the singleton instance
    public static GameManager Instance { get; private set; }

    // Enum defining game states
    public enum GameState { Title, Playing, Paused, GameOver }
    public GameState CurrentState { get; private set; }

    // Global game data
    public int Score { get; private set; }

    private void Awake()
    {
        // Singleton pattern implementation
        // If no instance exists, set this as the singleton
        if (Instance == null)
        {
            Instance = this;
            // Persist this object across scene loads
            DontDestroyOnLoad(gameObject);
        }
        // If instance already exists, destroy this duplicate
        else
        {
            Destroy(gameObject);
            return;
        }

        // Set initial state
        CurrentState = GameState.Title;
    }

    // Method to add score (callable from anywhere)
    public void AddScore(int amount)
    {
        if (CurrentState != GameState.Playing) return;
        Score += amount;
        // UIManager.Instance.UpdateScoreUI(Score); // Request UI update
    }

    // Method to change game state
    public void ChangeState(GameState newState)
    {
        if (CurrentState == newState) return;

        CurrentState = newState;

        // Execute state-specific processing
        switch (newState)
        {
            case GameState.Title:
                // Prepare title screen
                break;
            case GameState.Playing:
                // Prepare for gameplay
                Time.timeScale = 1f; // Resume time
                break;
            case GameState.Paused:
                // Handle pause
                Time.timeScale = 0f; // Stop time
                break;
            case GameState.GameOver:
                // Handle game over
                break;
        }
    }

    // Example calls from other scripts:
    // GameManager.Instance.AddScore(100);
    // GameManager.Instance.ChangeState(GameManager.GameState.GameOver);
}

Usage

  1. Create an empty GameObject and name it "GameManager."
  2. Attach the GameManager.cs script above.
  3. Make this GameManager object a Prefab and place it in the starting scene (splash or title screen)—it will persist as the single instance throughout the game.

DontDestroyOnLoad(gameObject); is the crucial command that prevents the GameManager from being destroyed when scenes change.

Game Manager Considerations

While the Singleton pattern is convenient, overuse can cause problems:

  • Tight Coupling: Since any script can access GameManager.Instance, many objects may become strongly dependent on the GameManager, reducing code reusability and testability.
  • Bloated Responsibilities: Cramming all game logic into the GameManager quickly creates an unmanageable mega-class. Better design separates concerns into specialized managers—ScoreManager, UIManager, AudioManager—with GameManager coordinating them.

Summary

The Game Manager is a powerful design pattern for organizing complex game structures.

  • Acts as a "command center" centralizing game-wide state, rules, and global data.
  • Uses Singleton pattern to guarantee exactly one instance in the game.
  • Uses DontDestroyOnLoad() to persist across scenes.
  • Defines game states with enum and manages transitions with switch statements.
  • Consider splitting into specialized managers to prevent responsibility bloat.

Simply introducing this basic Game Manager structure dramatically organizes your codebase, making feature additions and debugging much easier.