The core classes and their authority split, GameState in depth, actors versus components, and why composition beats deep inheritance.
The framework classes and their authority split — who owns what across the network:
| Class | Role | Network presence |
|---|---|---|
| GameMode | Rules of the match; spawns players; win and lose conditions | Server only, never replicated |
| GameState | Match-wide state — scores, timers, the player list | Replicated to all clients |
| PlayerState | Per-player state — name, score; survives pawn death | Replicated to all clients |
| PlayerController | Bridges a human to their pawn; owns input, camera and UI | Server + owning client |
| Pawn | The controllable entity — character, vehicle, floating camera | Replicated |
| Character | Pawn + capsule + skeletal mesh + movement component | Replicated |
Controller ↔ Pawn is the classic decoupling of input and decision from entity. A Controller possesses a Pawn; the Pawn doesn't know or care who controls it. That's what lets the same pawn be driven by a player or an AI without changing the pawn.
ACharacter earns its own class because the CharacterMovementComponent gives you networked bipedal locomotion out of the box — which is a significant amount of netcode you'd otherwise write yourself.
| Class | Has a transform? | Use it for |
|---|---|---|
| UActorComponent | No spatial data at all | Pure behavior — health, inventory, cooldown tracking |
| USceneComponent | Yes, adds a transform | Anything that needs a position in space |
An Actor's own transform is delegated to its RootComponent. The actor doesn't store a position itself — it asks its root.
Build something as a component when it's a reusable capability many different actors might need: health, inventory, targeting, interaction. Rather than a Health base class that forces everything into one hierarchy, you add a HealthComponent to any actor that can take damage.
Actor composition means building an actor's behavior by assembling components rather than inheriting from a deep chain of classes.
It's the classic "favor composition over inheritance" principle. Some inheritance is still fine — a Character is a Pawn — but distinct capabilities like health, inventory and abilities belong in components.
The table frames it, but GameState deserves the fuller picture, since a lot of architecture hangs off it. It's the replicated shared-truth holder — the one place every client is guaranteed to agree on the state of the match. Since GameMode is server-only, GameState is its client-visible counterpart: anything all clients need to read goes here.
| Class | Pairs with | Adds |
|---|---|---|
| AGameStateBase | AGameModeBase | Minimal base — PlayerArray, GameModeClass / SpawnedGameMode refs, replicated world time |
| AGameState | AGameMode | Match-state machine — MatchState, GetMatchState(), the WaitingToStart → InProgress → WaitingPostMatch flow |
Match the pair: GameModeBase with GameStateBase, or GameMode with GameState. On AGameModeBase you don't get the match-state machine, and the base is enough.
The GameState class is set by the GameMode, not on itself. Make the subclass, then point the GameMode at it:
AMyGameMode::AMyGameMode()
{
GameStateClass = AMyGameState::StaticClass();
}
In Blueprints: set the Game State Class dropdown in the GameMode's Class Defaults, or project-wide in Project Settings → Maps & Modes, or per-level in World Settings.
Use GetWorld()->GetGameState<AMyGameState>().
OnRep_* or HandleMatchHasStarted rather than assuming it exists at BeginPlay.
One GameState per world, spawned by the GameMode, with one replicated copy per client. Each UWorld gets its own — so multi-client PIE gives each window its own — and seamless travel briefly has a transition map's GameState before the destination's takes over.
Within one active world at one time it's a singleton by design, which is exactly why it's a safe home for shared state. Chapter 5 covers what should and shouldn't live there.
Answer each out loud before re-reading the chapter.