Game instance
GameMode gets destroyed and recreated on every level load; Pawns die and respawn; even
PlayerControllers can be recreated across a hard travel. UGameInstance is the one framework object
in this folder that survives all of it — it exists for exactly the data and logic that has no business
being tied to whichever level happens to be loaded right now.
Why this matters
Without GameInstance, "data that should survive a level change" (a loaded save file, matchmaking
state, a session token, cross-level UI like a persistent HUD widget) has nowhere clean to live —
everything else in the gameplay framework is scoped to a UWorld, and a UWorld is torn down and
rebuilt on every level transition. Reach for GameMode or a Pawn for this kind of data and it
vanishes the instant the player loads the next map.
Mental model
A UGameInstance is spawned once when the game (or one PIE client) starts, and lives until that game
instance shuts down — one per standalone game process, and critically, one separate UGameInstance
per Play-In-Editor client, which is why data you expect to be process-wide doesn't automatically work
that way under multi-client PIE. See World and levels for how PIE ends up with
multiple UWorlds in the same process.
The mechanics
Declaring and installing a custom GameInstance
UCLASS()
class MYGAME_API UMyGameInstance : public UGameInstance
{
GENERATED_BODY()
public:
virtual void Init() override;
virtual void Shutdown() override;
UPROPERTY(BlueprintReadOnly, Category = "Session")
FString PendingMatchId;
};
void UMyGameInstance::Init()
{
Super::Init();
// Runs once, before the first level loads — good place to load persistent
// save data or kick off a login/matchmaking flow.
}
void UMyGameInstance::Shutdown()
{
// Runs once, when the whole game session ends — the mirror image of Init().
Super::Shutdown();
}
A custom UGameInstance subclass is wired up in Project Settings under Maps & Modes → Game Instance
Class, not through spawning — the engine instantiates it automatically at startup.
Reaching it from gameplay code
if (UMyGameInstance* GI = GetGameInstance<UMyGameInstance>())
{
UE_LOG(LogTemp, Log, TEXT("Pending match: %s"), *GI->PendingMatchId);
}
AActor::GetGameInstance() (templated as GetGameInstance<T>()) reaches the instance through the
actor's UWorld, which is why an actor can always find "its" GameInstance even though the actor
itself is destroyed and recreated across level travel while the instance is not.
GameInstanceSubsystem: the preferred place for GameInstance-scoped managers
Rather than growing one UGameInstance subclass into a monolith, split cross-level managers into
UGameInstanceSubsystems — each is automatically instantiated alongside the GameInstance and
retrieved with GetSubsystem<T>(), with the same "no manual registration" ergonomics as any other
subsystem. See Subsystems for the full pattern; it's the accessor
subsystems.md itself links forward to this page for.
Gotchas
It's tempting to treat GameInstance as "the bigger GameMode," but they answer different questions:
GameMode is server-only, scoped to one UWorld, and re-created on every level load; GameInstance
exists on every machine (server and clients alike) and persists across level loads for the entire
session. Match-rule logic belongs on GameMode; cross-level session data belongs on GameInstance.
Data on GameInstance is not shared across multiple PIE clients in the same editor process — each PIE
client gets a fully separate UGameInstance. Code that assumes one GameInstance speaks for "the whole
test session" will behave differently in a two-client PIE test than in a real packaged multiplayer build
where each machine genuinely is separate.
See also
- World and levels — why a
UWorld, unlikeGameInstance, doesn't survive level travel. - Game mode and game state — the per-level counterpart
GameInstanceis deliberately not. - Framework overview — where GameInstance sits above the rest of this folder's classes.
- Subsystems —
UGameInstanceSubsystem, the preferred way to add GameInstance-scoped managers. - Epic — UGameInstance API reference