Skip to content

GameFlow System

PlatanoGames edited this page Feb 23, 2026 · 4 revisions

GameFlow System v1.0 — Zero-Enum State Management

"The first thing every game programmer builds is a state machine with an enum. The second thing they build is a bigger enum. The third thing they realize is that one enum cannot describe a game."


The Problem

Game state management in Unreal Engine typically starts with an enum. Five states. One switch. It works — for about two weeks.

Dimensionality. A game does not have one state. The UI can be in a menu while gameplay is active. The AI can be in a cinematic state while the camera is in free-look. Cramming orthogonal dimensions into a single enum produces combinatorial explosion — InGame_Playing_NormalCam_AIActive_UIHidden and forty-seven friends.

Extensibility. Enums are compile-time constants. Adding a state means modifying a header, recompiling every consumer, and updating every switch. For a framework plugin, this means users cannot add game states without modifying framework code.

Validation. Transition rules are scattered if-else chains, growing with every new state. No declarative way to say "from MainMenu, you can go to Loading or Options, but not to GameOver."

History. Enums carry no history. "Previous state" requires a separate variable. "Revert" requires a separate flag plus validation. Every project reimplements this from scratch.

The root cause: enums are rigid (compile-time), flat (single dimension), and opaque (no metadata, no history, no validation).


How PGX Solves It

PGX GameFlow replaces enums with GameplayTags and replaces the single state machine with eight independent channels.

GameplayTags as State Identifiers

GameplayTags are hierarchical string identifiers in UE5's tag system. Extensible via config files without recompilation. Hierarchical queries built in. Lightweight, hashable, serializable.

  Enum: EGameState::MainMenu          Tag: "PGX.Flow.Global.MainMenu"
  Adding a state: modify .h, recompile   Adding a state: Project Settings, done

Tags are hierarchical, enabling branch-based validation. A rule that says "allowed destinations under PGX.Flow.Global" automatically permits all current and future tags in that branch — including tags the framework author never imagined.

Eight Independent Channels

Instead of one state machine, GameFlow provides eight:

  Channel 0: GLOBAL        "What phase is the game in?"
  Channel 1: UI            "What is the UI doing?"
  Channel 2: CHARACTERS    "What are the characters doing?"
  Channel 3: AI            "What is the AI doing?"
  Channel 4: CAMERAS       "What is the camera doing?"
  Channel 5: SYSTEMS       "What are the backend systems doing?"
  Channel 6: LEVEL LOGIC   "What is the level's state?"
  Channel 7: ACTORS        "What are the actors doing?"

Each channel is an independent FSM: current state, history with timestamps, validation rules (Data Asset-driven), and its own delegate. Channels are orthogonal — changing the UI channel does not affect the AI channel.

  Single-enum:   InGame_Playing_NormalCam_AIActive_UIHidden  (47+ combos)

  Eight-channel: Global:InGame | UI:Hidden | Camera:Normal | AI:Active
                 Chars:Playing | Systems:Online | Level:Loaded | Actors:Spawned

The total state space is identical, but each channel's space is small, manageable, and independently extensible.


Architecture (ASCII Diagram)

              +----------------------------------------------+
              |            GameFlow Subsystem                 |
              |         (GameInstanceSubsystem)               |
              |                                              |
              |  +--------+--------+--------+--------+       |
              |  |  Ch 0  |  Ch 1  |  Ch 2  | ...    |       |
              |  | Global | UI     | Chars  | (x8)   |       |
              |  |        |        |        |        |       |
              |  | State  | State  | State  | State  |       |
              |  | History| History| History| History|       |
              |  +--------+--------+--------+--------+       |
              |                                              |
              |  +-------------------+  +-----------------+  |
              |  | Validation Engine |  | Batch Engine    |  |
              |  |                   |  |                 |  |
              |  | IsInBranch check  |  | Sequential      |  |
              |  | Allowed list      |  | Parallel        |  |
              |  | Disallowed query  |  | All-or-nothing  |  |
              |  +-------------------+  +-----------------+  |
              |                                              |
              |  +-------------------+  +-----------------+  |
              |  | History Manager   |  | Delegate Hub    |  |
              |  |                   |  |                 |  |
              |  | Per-channel log   |  | 8 dynamic (BP)  |  |
              |  | Timestamps        |  | 1 native (C++)  |  |
              |  | Revert support    |  |                 |  |
              |  +-------------------+  +-----------------+  |
              +----------------------------------------------+
                              |
              +---------------+---------------+
              |               |               |
      +-------v------+ +-----v--------+ +----v---------+
      | Rules Config | | Rules Config | | Rules Config |
      | DA: Global   | | DA: UI       | | DA: AI       |
      |              | |              | |              |
      | Tag -> Rule  | | Tag -> Rule  | | Tag -> Rule  |
      |  Allowed []  | |  Allowed []  | |  Allowed []  |
      |  Disallowed[]| |  Disallowed[]| |  Disallowed[]|
      |  CanRevert   | |  CanRevert   | |  CanRevert   |
      +--------------+ +--------------+ +--------------+

Transition Flow

  SetStateByTag(Global, "PGX.Flow.Global.InGame", Source)
      |
      v
  1. Branch Check -- Is tag under the channel's root? (string prefix match)
  2. Redundancy  -- Already in this state? -> RedundantState
  3. Rule Lookup -- Find rule for CURRENT state. No rule = ALLOW
  4. Whitelist   -- AllowedDestinations contains target? (empty = allow all)
  5. Blacklist   -- DisallowedTagQuery matches? (veto overrides whitelist)
  6. Apply       -- Push to history, set state, broadcast delegates
      |
      v
  Return: Success / ValidationError / RedundantState

Validation Model

Rules are declared per source state in Data Assets:

  +------------------+-------------------------------+-----------+
  | Source State      | Allowed Destinations          | Revert?   |
  +------------------+-------------------------------+-----------+
  | MainMenu         | [Loading, Options, Credits]   | No        |
  | Loading          | [InGame, MainMenu]            | No        |
  | InGame.Playing   | [InGame.Paused, GameOver]     | Yes       |
  | InGame.Paused    | [InGame.Playing, MainMenu]    | Yes       |
  | GameOver         | [MainMenu, Credits]           | No        |
  +------------------+-------------------------------+-----------+
  (Source states without rules = unrestricted)

Each rule specifies allowed destinations (whitelist), disallowed tag queries (blacklist veto), revert permission, and an error code. The key design choice: no rule = permissive. The system works with zero configuration and becomes restrictive only as rules are added.


Capabilities

State Transitions

Feature Description
Single transition Change one channel's state with full validation
Batch sequential Change multiple channels in order; stop on first failure
Batch parallel Change multiple channels; apply all or rollback all
Revert Return to the previous state (if the current rule permits)
Pre-validation Check if a transition would succeed without applying it

History

Each channel maintains an ordered history with timestamps. History enables revert (return to previous state with validation), audit trails, duration calculation (time between consecutive entries), and pattern detection ("how many pauses this session?").

Delegate Notifications

The delegate model is designed for both Blueprint and C++ consumers:

Delegate Count When
Per-channel dynamic 8 (one per channel) That specific channel changes state
Global native 1 (carries channel param) Any channel changes state

Blueprint code subscribes to the specific channel it cares about. C++ code can subscribe to the single native delegate and filter by channel parameter.

Query API

All queries are non-mutating and safe to call from any context:

Query Description
Get current state Current tag for a specific channel
Get last state Previous tag for a specific channel
Get channel history Full history array with timestamps
Get allowed transitions Valid destinations from the current state
Is current state Boolean check against a specific tag
Can change Pre-validation without applying the transition
Can revert Check if revert is allowed from the current state
Channel name String name for a channel enum value (static utility)

Configuration

Rules Config Data Assets

Each channel's validation rules live in a separate Data Asset:

  Content Browser > Right-click > PGX > GameFlow
  -> Creates a Flow Rules Config asset

  1. Select the channel this rules config governs
  2. Add rules per source state:
     - Source tag
     - Allowed destinations (leave empty for unrestricted)
     - Disallowed tag queries (blacklist veto)
     - Revert permission flag
  3. Done. Auto-discovered at initialization.

Multiple rules configs can exist for the same channel. The subsystem discovers all of them and merges the rule sets. This allows modular rule definition — base rules in one asset, DLC-specific rules in another.

Tag Structure

Tags follow a hierarchical convention: PGX.Flow.{Channel}.{State}. The framework provides root branches (PGX.Flow.Global, PGX.Flow.UI, etc.). Game projects add leaf tags via Project Settings > Gameplay Tags. No framework modification. No recompilation.

Zero-Config Default

Without any Rules Config Data Assets, all transitions are allowed on all channels. The system is fully functional with zero configuration — it becomes a tag-based state tracker with history. Validation is opt-in, added incrementally as the project's state machine solidifies.


Editor Tooling

GameFlow Inspector

  +----------------------------------------------------------+
  |  PGX GameFlow Inspector                           [Pin]  |
  +----------------------------------------------------------+
  |                                                          |
  |  CHANNEL STATES (live, color-coded)                      |
  |                                                          |
  |  [=] Global     PGX.Flow.Global.InGame.Playing           |
  |  [=] UI         PGX.Flow.UI.HUD                          |
  |  [=] Characters PGX.Flow.Characters.Exploring             |
  |  [=] AI         PGX.Flow.AI.Combat                        |
  |  [=] Cameras    PGX.Flow.Cameras.ThirdPerson              |
  |  [=] Systems    PGX.Flow.Systems.Online                   |
  |  [=] LevelLogic PGX.Flow.LevelLogic.Exploration           |
  |  [=] Actors     PGX.Flow.Actors.Active                    |
  |                                                          |
  +----------------------------------------------------------+
  |                                                          |
  |  TRANSITION HISTORY                                      |
  |  +------+----------+---------------------------+------+  |
  |  | Time | Channel  | Transition                | Src  |  |
  |  +------+----------+---------------------------+------+  |
  |  | 14:32| Global   | Loading -> InGame.Playing | Sys  |  |
  |  | 14:32| UI       | Loading -> HUD            | Sys  |  |
  |  | 14:33| AI       | Idle -> Combat            | NPC  |  |
  |  | 14:34| Cameras  | Default -> ThirdPerson    | Ctrl |  |
  |  +------+----------+---------------------------+------+  |
  |                                                          |
  +----------------------------------------------------------+
  |  Channels: 8/8 active | History: 47 entries              |
  +----------------------------------------------------------+

The inspector provides:

  • Channel state panel: All eight channels displayed simultaneously with their current tags. Each channel has its own color (Gold for Global, Sky Blue for UI, Green for Characters, Red-Orange for AI, Purple for Cameras, Gray for Systems, Teal for Level Logic, Orange for Actors).
  • Transition history: Chronological list of all transitions across all channels, with timestamps, source references, and transition details.
  • Channel detail: Click a channel to see its full history, current validation rules, and allowed transitions from the current state.

Integration Points

For Game Developers

The typical integration flow:

  1. Define your game states as GameplayTags (Project Settings > Gameplay Tags)
  2. (Optional) Create Rules Config Data Assets for channels that need validation
  3. In Blueprints or C++, set states and subscribe to channel delegates
  4. Query current states to drive game logic

No inheritance required. No base class extension. No framework class modification.

For Other PGX Systems

Several PGX systems consume GameFlow state:

  • Loading: Checks the Global channel before initiating level transitions
  • PSO: Uses GameFlow state to activate appropriate pipeline caches
  • Audio: Can react to game state changes for ambient sound management
  • Save: Can tie auto-save triggers to game state transitions

These integrations use the framework's message bus — they do not create direct dependencies on the GameFlow plugin.

Delegate Subscription

Blueprint: subscribe to channel-specific delegates (e.g., "OnFlowGlobalChanged" fires with FlowTag + Source). C++: subscribe to the single native delegate that carries a Channel parameter for filtering.


Console Commands

Command Description
pgx.gameflow.status Print all 8 channels with their current state tags
pgx.gameflow.set Set a channel's state from the console (debug)
pgx.gameflow.canchange Pre-validate a transition without applying it
pgx.gameflow.history Print a channel's transition history
pgx.gameflow.revert Revert a channel to its previous state
pgx.gameflow.rules Print the validation rules for a channel

Blueprint Nodes (15)

Organized into three categories:

Category Nodes
Core Set State By Tag, Revert To Previous, Get Current Flow Tag, Is Current Flow Tag
Query Get Last Flow Tag, Get Channel History, Get Allowed Transitions, Get Allowed From Current, Get Channel Name, Is Initialized
Advanced Set Batch Sequential, Set Batch Parallel, Can Change By Tag, Can Batch Change, Check Can Revert

Why This Matters

State management is the skeleton of every game. Every system in the project — rendering, audio, input, AI, UI — needs to know "what is the game doing right now?" The answer to that question determines which systems are active, which inputs are consumed, which audio plays, which UI is visible.

When that answer lives in an enum, the skeleton is rigid. Adding a bone requires surgery on every joint. When the game grows beyond the enum's capacity, the result is either an explosion of compound states or an ad-hoc secondary state system that contradicts the primary one.

GameplayTags eliminate the rigidity. Tags are extensible without recompilation. The framework defines the channels and the validation pattern. The game project defines the states. A new state is a config entry, not a code change.

Eight independent channels eliminate the dimensionality problem. Instead of modeling the product of all state dimensions as a single flat enum, each dimension is an independent state machine. The UI team manages UI states. The AI team manages AI states. Neither team's changes affect the other.

Data Asset-driven validation eliminates the procedural validation problem. Transition rules are declarative, auditable, and modular. A designer can look at a Rules Config and understand exactly which transitions are legal without reading code. A new rule is an asset change, not a code change.

History and revert eliminate the state-tracking boilerplate. Every channel remembers where it has been. Reverting to the previous state is a one-call operation with built-in validation.

The result is a state management system where the game project never modifies framework code. States are tags. Rules are assets. Channels are independent. History is automatic. The skeleton is flexible.


Resumen en Espanol

El sistema GameFlow v1.0 es un gestor de estado basado en GameplayTags con 8 canales independientes, validacion por Data Asset, historial, y revert.

El problema: En UE5, los state machines se construyen con enums. Agregar un estado requiere recompilacion. Los estados son planos — no puedes separar estado de UI del estado de AI. La validacion es manual (cadenas if-else). No hay historial.

La solucion: GameplayTags en lugar de enums. 8 canales independientes (Global, UI, Characters, AI, Cameras, Systems, LevelLogic, Actors). Cada canal es un FSM independiente con su propio estado, historial, y reglas de validacion.

Tags extensibles: El framework define las ramas raiz. El proyecto del juego agrega sus estados como tags hijos via Project Settings. Cero modificacion de codigo del framework. Cero recompilacion.

Validacion declarativa: Data Assets de reglas por canal. Destinos permitidos (whitelist), queries de veto (blacklist), permiso de revert. Sin regla = todo permitido (default permisivo).

Historial automatico: Cada canal recuerda todos sus estados anteriores con timestamps. Revert a estado anterior es una llamada con validacion integrada.

En el editor: Inspector con 8 canales coloreados, historial de transiciones cruzado, detalle por canal. 6 comandos de consola, 15 nodos Blueprint en 3 categorias.

Filosofia de diseno: Los estados son tags (extensibles sin compilar). Las reglas son assets (auditables sin leer codigo). Los canales son independientes (el equipo de UI no afecta al equipo de AI). El esqueleto del juego es flexible por construccion.

Clone this wiki locally