Imported from ziyarduc/Baran-bk-game (
.agents/skills/gameplay-framework/SKILL.md). Install upstream withnpx skills add ziyarduc/Baran-bk-game --skill gameplay-framework. Copyright stays with the author.
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).
// 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++
// 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 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:
// 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 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.
// 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 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 requiringrecompile. See
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 an
AGameModeBasesubclass. -
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 (seeumg-and-slate). -
APlayerStateoutlives the pawn: the state persists inGameState.PlayerArrayeven whenthe 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 —
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,
HandleStartingNewPlayeroverridepoints.
Cross-references: actors-and-components (AActor lifecycle), character-and-movement
(ACharacter / UCharacterMovementComponent detail), subsystems (UGameInstanceSubsystem),
networking-and-replication (replication mechanics), blueprint-cpp-integration
(exposing C++ to designers).
Bakırköy BR Core Constraints & MVP Directives
When applying this skill to the Bakırköy BR project, you MUST strictly adhere to:
1. The 7 Core Constraints
- No Interior Spaces: Buildings are exterior-only collision volumes. No interior rooms, furniture, or interior NavMesh. Rooftop/terrace access is strictly via external stairs, ladders, or fire escapes.
- Solo BR Only: First prototype supports Solo mode only. No squad logic, duos, revives, DBNO (Down-But-Not-Out), or team chat.
- Server-Authoritative: Dedicated server validates and executes all gameplay state changes (HP, Shield, ammo, damage, storm, eliminations). Client predicts locally, server reconciles.
- 3rd Person Camera: Over-the-shoulder perspective only. ADS tightens camera FOV and spring arm length, but NEVER switches to 1st person.
- 3 Build Materials: Exactly 3 materials: Moloz (Debris: 60 start / 100 max HP), Tuğla (Brick: 80 start / 200 max HP), and Çelik (Steel: 100 start / 350 max HP). Never 4 materials.
- Hybrid Hit Detection: AR, SMG, Shotgun, Sniper use server Hit-Scan line traces (
LineTraceSingleByChannel). Rocket Launcher uses Chaos Projectile physics (ABRProjectileactor with 35 m/s velocity and radial splash damage). - Mandatory C++
BRPrefix: Every gameplay class, struct, and enum MUST be prefixed withBR(e.g.ABRCharacter,UBRHealthComponent,FBRWeaponData,EBRBuildMaterial).
2. Demo 1 Playable MVP Directives
- 10 Bots Test Scenario: AI count is strictly limited to 10 bots. GameMode and AI logic must be optimized for this 10-bot vertical slice.
- 2 Weapon Prototypes: Initial loot pool and combat mechanics test the hybrid hit detection using exactly 2 weapons: 1 Assault Rifle (Hit-Scan) and 1 Rocket Launcher (Projectile physics + splash damage).
- Dual GameModes: Support 2 distinct playable GameModes: Mode 1 Free-For-All (FFA / Deathmatch with score/time limit) and Mode 2 Classic Battle Royale (Last Man Standing with shrinking storm circle).
- Building System Paused: Building system is paused for Demo 1. Players and bots rely entirely on natural environment cover (vehicles, alleys, street walls).
Domain Adaptation: Gameplay Framework & GameModes
- Dual GameModes:
ABRGameMode_FFA: Free-For-All deathmatch with score/kill limit and match countdown timer.ABRGameMode_BR: Classic Battle Royale with 7-phase shrinking storm circle and Last Man Standing victory condition.
- Solo BR Architecture:
ABRGameStateandABRPlayerStatetrack individual players and bots only. No squad structures, team IDs, or shared inventories. - 10-Bot Match Loop: Server spawns 1 player and 10 bots across outdoor spawn points.
- Server Authority: All state transitions (match state, storm phase, score, eliminations) execute strictly on the server.
