Gameplay framework
The gameplay framework is the set of base classes Unreal spawns and wires together to run a game. Choosing the right class for each piece of logic is the single most important design decision in UE gameplay code.
When to use this skill
- Creating a new game mode, game state, player controller, pawn, player state, or HUD class.
- Implementing player login, spawn, respawn, or possession logic.
- Deciding where a piece of state lives (server-only vs replicated vs per-player vs cross-level).
- Setting up default classes in C++ and exposing them to designers via Blueprint subclasses.
- Wiring the spawn/possession flow or implementing a match-state machine.
The classes and their roles
| Class | Lives where | Core responsibility | Replicated? |
|---|---|---|---|
UGameInstance | one per game, persists across level loads | app-lifetime state, subsystems, online sessions | no |
AGameModeBase | server only | rules, default classes, login/spawn flow | no |
AGameMode | server only | adds match-state machine on top of Base | no |
AGameStateBase | server + all clients | game-wide visible state (score, timer, match phase) | yes |
AGameState | server + all clients | adds MatchState replication to Base | yes |
APlayerController | server + owning client | input, camera, UI, possession bridge | to owner only |
APlayerState | server + all clients | per-player replicated data (name, score, team) | yes |
APawn | server + clients | physical avatar in the world | yes |
ACharacter | server + clients | bipedal pawn with movement, capsule, skeletal mesh | yes |
AHUD | owning client only | legacy canvas overlay (prefer UMG for real UI) | local |
Class hierarchy (verified in 5.8 source):
AGameModeBase : public AInfo(GameModeBase.h:47);AGameMode : public AGameModeBase(GameMode.h:35).AGameStateBase : public AInfo(GameStateBase.h:32);AGameState : public AGameStateBase(GameState.h:16).APlayerController : public AController(PlayerController.h:262);AController : public AActor(Controller.h:40).APawn : public AActor(Pawn.h:43);ACharacter : public APawn(Character.h:338).APlayerState : public AInfo(PlayerState.h:41);AHUD : public AActor(HUD.h:36).UGameInstance : public UObject, public FExec(Engine/GameInstance.h:151).
Base vs non-base: AGameModeBase and AGameStateBase are the lean modern defaults.
AGameMode/AGameState add the match-state machine (MatchState, WaitingToStart,
InProgress, WaitingPostMatch, …) inherited from UE3-era multiplayer. Start from *Base
unless you need match states.
Ownership and relationship map
UGameInstance (persists across levels) └── UWorld (current level) ├── AGameModeBase (server only — rules and spawning) ├── AGameStateBase (replicated — global visible state) │ └── PlayerArray: TArray<APlayerState*> (one per connected player, replicated) └── per player: APlayerController ──possesses──> APawn / ACharacter │ └── movement, mesh, collision components └── PlayerState (also in GameState.PlayerArray)
Key accessors verified in 5.8 source:
GetWorld()->GetGameState<T>()— replicated on all machines.GetWorld()->GetAuthGameMode<T>()— server only; returns null on clients.GetGameInstance<T>()— available via anyUObjectwith a valid outer chain.AController::GetPawn()(Controller.h:233);APawn::GetController()(Pawn.h:264).APawn::GetPlayerState<T>()(Pawn.h:193);AController::PlayerState(Controller.h:50).
Server spawn and login flow
On the server, AGameModeBase drives the sequence when a player joins:
InitGame(GameModeBase.h:62) — called before any actorPreInitializeComponents; spawn helper classes here.PreLogin(GameModeBase.h:293) — reject players by settingErrorMessagebefore any state is allocated.Login(GameModeBase.h:323) — creates and returns theAPlayerController; spawnsAPlayerState.PostLogin(PC)(GameModeBase.h:326) — first safe place to call replicated functions on the PC.HandleStartingNewPlayer(PC)(GameModeBase.h:361) — override in Blueprint or C++ to control what happens next.RestartPlayer(PC)(GameModeBase.h:435) →FindPlayerStart→SpawnDefaultPawnAtTransform→PC->Possess(Pawn).
Full login flow with seamless travel and OnPostLogin details:
references/init-and-login-flow.md.
Possession
AController::Possess(APawn*) is final in 5.8; override OnPossess instead
(Controller.h:299). The symmetric unpossess override is OnUnPossess (Controller.h:306).
cpp// Override OnPossess to react to possession — NOT Possess() which is final void AMyController::OnPossess(APawn* InPawn) { Super::OnPossess(InPawn); // Safe to access InPawn here; PlayerState is already assigned } // On the pawn side, PossessedBy fires when a controller takes control void AMyPawn::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); // Bind abilities, movement, etc. that depend on the controller }
A controller can possess different pawns over its lifetime (death/respawn, vehicle enter/exit).
Do not store gameplay state on the pawn that must survive across possessions — use PlayerState
or PlayerController instead.
Setting default classes in C++
cpp// MyGameMode.h UCLASS() class MYGAME_API AMyGameMode : public AGameModeBase { GENERATED_BODY() public: AMyGameMode(); }; // MyGameMode.cpp #include "MyGameMode.h" #include "MyCharacter.h" #include "MyPlayerController.h" #include "MyPlayerState.h" #include "MyGameState.h" AMyGameMode::AMyGameMode() { DefaultPawnClass = AMyCharacter::StaticClass(); PlayerControllerClass = AMyPlayerController::StaticClass(); PlayerStateClass = AMyPlayerState::StaticClass(); GameStateClass = AMyGameState::StaticClass(); // HUDClass — set if you use legacy AHUD; omit for pure UMG }
These five TSubclassOf<> properties are declared on AGameModeBase with
EditAnywhere, NoClear, BlueprintReadOnly (GameModeBase.h:87–108). Assign them in the
constructor so Blueprint subclasses can still override them in Blueprints editor defaults.
Then wire the GameMode per-project (Project Settings → Maps & Modes →
DefaultGameMode/GlobalDefaultGameMode in DefaultEngine.ini
[/Script/EngineSettings.GameMapsSettings]), or per-level (World Settings → GameMode Override).
Where does my logic go? (decision guide)
| Logic | Belongs in |
|---|---|
| Rules, win/lose, who spawns where, scoring authority | AGameModeBase — server only |
| State everyone must see (scores, timer, match phase) | AGameStateBase / AGameState |
| Per-player replicated facts (name, team, kills) | APlayerState |
| Input handling, camera, opening menus, player intent | APlayerController |
| Physical movement, abilities of the avatar | APawn / ACharacter + components |
| Cross-level or app-lifetime (save game, audio settings, online session) | UGameInstance / a UGameInstanceSubsystem |
If it must survive a level load → UGameInstance or UGameInstanceSubsystem
(see ue-subsystems). If only the server may decide it → AGameModeBase.
GameInstance and subsystems
UGameInstance is created once on engine launch and destroyed when the application exits,
surviving all level transitions. Override Init (GameInstance.h:217) and
Shutdown (GameInstance.h:224) for setup/teardown. Access it from any actor via
GetGameInstance<UMyGameInstance>().
For modular, dependency-isolated systems with the same lifetime:
cpp// Subsystem declared as UCLASS(), auto-created by GameInstance class UMyOnlineSubsystem : public UGameInstanceSubsystem { GENERATED_BODY() public: virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; }; // Access from any actor UMyOnlineSubsystem* Sub = GetGameInstance()->GetSubsystem<UMyOnlineSubsystem>();
GetSubsystem<T>() is declared on UGameInstance (GameInstance.h:440). See ue-subsystems.
PlayerState — per-player replicated data
APlayerState is created by Login and added to AGameStateBase::PlayerArray. It replicates
to all clients, making it the correct place for data every machine needs about each player.
cpp// MyPlayerState.h UCLASS() class MYGAME_API AMyPlayerState : public APlayerState { GENERATED_BODY() public: UPROPERTY(Replicated, BlueprintReadOnly, Category=Player) int32 TeamIndex = 0; virtual void GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps) const override; };
Built-in replicated fields on APlayerState: Score (getter GetScore, setter SetScore,
PlayerState.h:312/318), PlayerNamePrivate (accessed via GetPlayerName/SetPlayerName,
PlayerState.h:235/228), PlayerId, CompressedPing, bIsSpectator, bIsABot.
Network roles
AGameModeBaseexists only on the server; never put client logic there.AGameStateBase/APlayerStateare replicated — read on any machine, only write on server.APlayerControllerreplicates to its owning client only — good for client RPCs and UI.APawn/ACharacterreplicate to all clients;HasAuthority()determines who may change state.- Use
HasAuthority()orGetLocalRole() == ROLE_Authorityto branch server vs client.
See ue-networking-and-replication for property/RPC mechanics.
C++ base + Blueprint subclass pattern
The idiomatic setup:
- Put logic, replicated state, and core API in C++ (
AMyGameMode,AMyCharacter, …). - Expose designer-facing defaults via Blueprint subclasses (
BP_GameMode,BP_Character, …). - Set the BP subclasses as defaults in Project Settings or World Settings.
- Use
UPROPERTY(EditDefaultsOnly)for numbers/assets designers should tweak without requiring recompile. Seeue-blueprint-cpp-integration.
Gotchas
GetAuthGameMode()on a client returns null; always guard withHasAuthority().Possessisfinalsince UE 4.22; overrideOnPossess/OnUnPossess(Controller.h:299/306).- Persistent state on the Pawn is lost on death/respawn; use
APlayerStateorAPlayerController. - Possession ≠ spawn: a controller can possess different pawns over time; don't assume 1:1 lifetime.
AGameModevsAGameModeBasemismatch:MatchStatemachinery only exists inAGameMode; don't reference it from anAGameModeBasesubclass.DispatchPostLogindeprecated in 5.6: overrideOnPostLogin(AController*)instead (GameModeBase.h:334 — note the UE_DEPRECATED annotation at :329).- HUD for modern UI:
AHUDis legacy canvas drawing; build real UI with UMG (seeue-umg-and-slate). APlayerStateoutlives the pawn: the state persists inGameState.PlayerArrayeven when the pawn is destroyed; that is by design so scores survive death.- GameInstance is not replicated: the server and each client each have their own independent instance. Do not use it as a shared-state store in multiplayer.
Version notes
AGameModeBaseintroduced in UE 4.14; older projects may subclassAGameModedirectly.Possess/UnPossessmarkedvirtual finalin UE 4.22; useOnPossess/OnUnPossess.DispatchPostLogindeprecated in UE 5.6; the replacement isOnPostLogin(protected virtual).- The framework is stable across UE 5.x; line numbers in citations drift between patch releases but header paths and class names are stable.
References & source material
Engine source (UE 5.8, under Engine/Source/Runtime/Engine/Classes/):
GameFramework/GameModeBase.h—AGameModeBase:47,InitGame:62,InitGameState:69,DefaultPawnClass:108,PlayerControllerClass:96,PlayerStateClass:100,GameStateClass:92,HUDClass:104,PreLogin:293,Login:323,PostLogin:326,OnPostLogin:334,HandleStartingNewPlayer:361,RestartPlayer:435,SpawnDefaultPawnAtTransform:461,ChoosePlayerStart:405,GetDefaultPawnClassForController:84.GameFramework/GameMode.h—AGameMode:35,MatchStatenamespace:16,GetMatchState:43,StartMatch:51,EndMatch:55,SetMatchState:72.GameFramework/GameStateBase.h—AGameStateBase:32,PlayerArray:55,GetServerWorldTimeSeconds:72,HasBegunPlay:76,AddPlayerState:112,RemovePlayerState:115.GameFramework/GameState.h—AGameState:16,MatchState:35,ElapsedTime:57.GameFramework/Controller.h—AController:40,PlayerState:50,GetPawn():233,Possess(final):284,UnPossess(final):288,OnPossess:299,OnUnPossess:306.GameFramework/PlayerController.h—APlayerController:262,PlayerCameraManager:287,MyHUD:280,SetInputMode:1649,SetShowMouseCursor:2168.GameFramework/Pawn.h—APawn:43,GetController():264,GetPlayerState<T>():193,PossessedBy:374,UnPossessed:381.GameFramework/Character.h—ACharacter:338.GameFramework/PlayerState.h—APlayerState:41,Score:49,GetScore:312,SetScore:318,PlayerNamePrivate:162,GetPlayerName:235,SetPlayerName:228.GameFramework/HUD.h—AHUD:36,PlayerOwner:42,bShowHUD:50.Engine/GameInstance.h—UGameInstance:151,Init:217,Shutdown:224,GetSubsystem<T>():440.
Official docs (UE 5.8, all fetched and confirmed live):
- Gameplay Framework overview — https://dev.epicgames.com/documentation/unreal-engine/gameplay-framework-in-unreal-engine
- Game Mode and Game State — https://dev.epicgames.com/documentation/unreal-engine/game-mode-and-game-state-in-unreal-engine
- Player Controllers — https://dev.epicgames.com/documentation/unreal-engine/player-controllers-in-unreal-engine
- Pawn — https://dev.epicgames.com/documentation/unreal-engine/pawn-in-unreal-engine
- Controllers — https://dev.epicgames.com/documentation/unreal-engine/controllers-in-unreal-engine
- Gameplay Framework Quick Reference — https://dev.epicgames.com/documentation/unreal-engine/gameplay-framework-quick-reference-in-unreal-engine
Deep-dive references in this skill:
- references/gamemode-and-state.md — GameMode class hierarchy, match-state machine, GameState replication, setting default classes per-map.
- references/controllers-and-pawns.md — controller lifecycle, possession API, PlayerController responsibilities, Pawn vs Character choice, PlayerState data design.
- references/init-and-login-flow.md — step-by-step server
login/spawn sequence, seamless travel, respawn patterns,
HandleStartingNewPlayeroverride points.
Cross-references: ue-actors-and-components (AActor lifecycle), ue-character-and-movement
(ACharacter / UCharacterMovementComponent detail), ue-subsystems (UGameInstanceSubsystem),
ue-networking-and-replication (replication mechanics), ue-blueprint-cpp-integration
(exposing C++ to designers).


