In this lesson, you will learn how to use a Stack<T> data structure to manage overlapping game states, allowing you to pause the game or open menus without losing your previous progress.
Why use a Stack for States?
Standard State Machines are great for simple transitions, but they often struggle when you need to "interrupt" a state. For example, if your player is in the GameplayState and hits "Pause," you don’t want to destroy the gameplay data; you want to "push" the PauseState on top of it. Once the player resumes, you "pop" the pause state, revealing the active game state underneath.
Implementing the Stack Controller
The core of this pattern is a centralized manager that holds a Stack<IGameState>. When you need to change states, you don’t just swap them; you push or pop them.
public class StateManager : MonoBehaviour
{
private Stack<IGameState> _stateStack = new Stack<IGameState>();
public void PushState(IGameState newState)
{
if (_stateStack.Count > 0)
_stateStack.Peek().OnPause(); // Pause the current state
_stateStack.Push(newState);
newState.OnEnter();
}
public void PopState()
{
if (_stateStack.Count > 0)
{
_stateStack.Pop().OnExit();
if (_stateStack.Count > 0)
_stateStack.Peek().OnResume(); // Return to the previous state
}
}
}
The Logic Flow
- Pushing: When you open a sub-menu (like Inventory), the
PushStatemethod callsOnPause()on the current state. This allows the game to stop simulation or disable input before layering the new menu on top. - Popping: When you close the menu,
PopStatecleans up the current state and triggersOnResume()on the state that was waiting underneath, allowing it to pick up exactly where it left off.
By utilizing this LIFO (Last-In, First-Out) structure, you create a robust "drill-down" navigation system that prevents memory leaks and ensures your game state remains consistent during interruptions.
Key Takeaway: Use a state stack to layer gameplay contexts, allowing you to pause and resume previous states seamlessly without destroying or re-initializing them.