Skip to content

Score, Objectives, and Win/Loss State

Score and objectives are not widget variables. They are gameplay state with an owner, an audience, and a lifetime. Pick those first, then the Blueprint graph gets much smaller.

The one-minute version

  • GameMode decides rules. It is the server-side place that answers "did this kill count?", "is the objective complete?", and "has the match ended?"
  • GameState exposes public match state. Put shared values there when every player, spectator, or HUD needs to know the current phase, team score, wave, objective progress, or winner.
  • PlayerState exposes public per-player state. Put score, kills, deaths, assists, team, ready state, and player-facing match identity there when the value belongs to one player and should survive pawn replacement.
  • Placed objective or wave managers coordinate level-specific work. A generator set, room puzzle, race checkpoint route, or arena wave can own its local process, then report the public result to GameState or the decision to GameMode.
  • Widgets read and request. HUDs, scoreboards, end screens, prompts, and floating labels should display GameState/PlayerState/objective data; they should not be the truth.
  • SaveGame is a snapshot, not live state. Copy durable results into a SaveGame object when the run ends, a checkpoint is reached, or the player explicitly saves.

The ownership split

The beginner mental model is:

flowchart TD
    Event[Kill, pickup, checkpoint, generator, wave clear] --> Rule[GameMode<br/>decides rules on the server]
    Event --> Manager[Objective / Wave Manager<br/>coordinates level-specific actors]
    Rule --> GS[GameState<br/>public match state]
    Rule --> PS[PlayerState<br/>public per-player state]
    Manager --> Rule
    Manager --> GS
    GS --> UI[HUD / scoreboard / end screen<br/>local presentation]
    PS --> UI
    Rule --> Save[SaveGame write<br/>durable snapshot when appropriate]

GameMode is the authority for rules. GameState and PlayerState are the public records clients can read. A manager actor is useful when the objective is a named thing in the level with placed participants. UI turns those records into bars, rows, markers, text, sounds, and menus.

The quick path is to put every "global" score variable in GameMode and every display value in the widget. The concrete cost is that remote clients cannot read the real GameMode instance, widgets are local only, and pawn/widget lifetime bugs erase state during death, respawn, travel, or late UI creation.

What belongs where

State or behavior First owner to consider Why
Win/loss decision GameMode It is a rule decision. In multiplayer, only the server should decide it.
Match phase enum: Waiting, Playing, Won, Lost, Finished GameState Every player and UI needs the same public phase.
Single-player current run score PlayerState or GameState Use PlayerState if it belongs to the player; GameState if it is the run/session total.
Per-player score, kills, deaths, assists PlayerState It belongs to one player and should survive pawn death.
Team score GameState It is match-wide public state, not one player's body state.
Objective progress: 2 of 3 generators GameState or an objective manager plus GameState summary The manager can coordinate the generators; GameState exposes the public result.
Objective participants: generators, checkpoints, spawners Placed manager actor or participant actors/components These are level actors with references, collision, and local behavior.
End-screen widget PlayerController / HUD / local widget path UI is local presentation; it should read the final public state.
High score, unlocked stars, completed mission IDs SaveGame This must survive quitting the game.
Cross-level temporary run data GameInstance, then copied into new map owners It survives Open Level, but it is not replicated and is not saved to disk by itself.

This table is about ownership, not class purity. A small single-player prototype can start simpler. The rule is still the same: put the value where its lifetime and audience match.

Match phase first, details second

Many first projects create several booleans:

bGameStarted
bGameOver
bPlayerWon
bCanRespawn
bShowEndScreen

That looks simple until two booleans disagree. Prefer one phase enum:

EMatchPhase
    WaitingToStart
    Playing
    Won
    Lost
    Finished

Then put the public phase where the UI can read it:

BP_MatchGameState
    MatchPhase: EMatchPhase, RepNotify if networked
    WinningTeam: TeamId or None
    ObjectiveSummary: FObjectiveSummary

GameMode sets the phase when rules say it changed:

pseudocode - project logic, not engine source

function CheckForEndCondition():
    if MatchPhase != Playing:
        return

    if RequiredObjectivesComplete():
        GameState.MatchPhase = Won
        GameState.WinningTeam = PlayerTeam
        SaveMissionResultIfNeeded()
        NotifyPlayersOrLet HUD react to GameState

    else if AllPlayersDefeated():
        GameState.MatchPhase = Lost
        SaveMissionResultIfNeeded()
        NotifyPlayersOrLet HUD react to GameState

The phase is the public fact. The end screen is just one local response to that fact.

Score shapes

Per-player score

Use PlayerState when score belongs to a player:

Enemy death runs once on the server
-> GameMode AwardKill(KillerController, DeadEnemy)
-> KillerController Get PlayerState
-> BP_MatchPlayerState.Score += Points
-> BP_MatchPlayerState.Kills += 1
-> CheckForEndCondition()

The pawn can die and be replaced. The PlayerState remains the public player record for the connected player.

Team score

Use GameState when score belongs to the match or team:

Server confirms Team A captured zone
-> GameMode validates capture rule
-> GameState.TeamAScore += 1
-> GameState.ObjectiveSummary = updated public summary
-> CheckForEndCondition()

If one player should also receive credit, update that player's PlayerState in the same server-owned scoring path.

Single-player score

Single-player still has lifetime questions:

Score need Reasonable first home
Score for the current pawn body only Pawn or component, if losing it on death is intended.
Score for the current player/run PlayerState or GameState.
Score visible in HUD after respawn PlayerState, GameState, or a controller-owned model read by the HUD.
Score that survives changing maps GameInstance during travel, then rebuild into the new map's owner.
High score or mission result after quitting SaveGame.

Do not store score in a widget because it is easy to see there. The widget may be destroyed and recreated without changing the gameplay truth.

Objective progress

Objectives usually have two layers:

  1. The local process: which levers are on, which enemies are alive, which checkpoints were crossed, which generators are repaired.
  2. The public summary: "2 / 3 generators", "Wave 4", "Payload 72%", "Objective complete", "Defeat".

A placed objective manager actor often owns the local process:

BP_GeneratorObjectiveManager
    Generators: array of BP_Generator references
    RequiredCount: 3
    CompletedCount: 0

Generator broadcasts OnRepaired
-> manager increments CompletedCount once
-> manager updates GameState.ObjectiveSummary
-> if CompletedCount >= RequiredCount:
       manager tells GameMode ObjectiveCompleted(Self)

GameState should expose the summary when HUD, markers, scoreboards, or clients need it. GameMode should decide whether that completion ends the match, awards score, opens travel, or starts a new phase.

End screens

An end screen is local UI. The result it presents is gameplay state.

Beginner-safe shape:

Server GameMode decides match ended
-> GameState.MatchPhase = Won or Lost
-> GameState.FinalScore / WinningTeam / ObjectiveSummary updated

Each local PlayerController or HUD:
-> observes GameState phase change
-> creates WBP_EndScreen locally
-> reads GameState and local PlayerState values
-> offers Restart / Continue / Main Menu buttons

In single-player, this may all happen in one process. In multiplayer, the same split prevents a remote client from trying to read server-only GameMode or a host-only widget.

SaveGame and travel

SaveGame is for durable values:

FMissionSaveRecord
    MissionId
    BestScore
    bCompleted
    StarsEarned
    ObjectivesCompleted

Write those values at deliberate points:

  • when a checkpoint is accepted;
  • when a mission ends;
  • when the player confirms a save;
  • when returning to a menu;
  • when a roguelike run commits its result.

Do not save live actor references, widget references, timer handles, or manager actor pointers. Save stable IDs and plain values. On load, the new map finds or creates the live actors again and feeds them the saved plain data.

GameInstance is the temporary bridge for data across Open Level. It is not a disk save, and in multiplayer it exists separately on each machine.

Multiplayer authority

For networked projects, use the same rule every time:

Client does a thing
-> client sends an owned Run on Server request when gameplay state should change
-> server validates the request
-> GameMode / manager / authoritative actor changes the real state
-> GameState and PlayerState replicate the public facts
-> each client updates local UI from those facts

Clients can play temporary local feedback, but they should not award real score or declare victory because their local graph thought something happened.

Late-joining clients need current replicated state, not a one-time event they missed. That is why final score, phase, objective progress, and player score should be stored as state, not only broadcast as a past message.

Failure modes

Symptom Likely ownership problem
Host sees score, clients see zero Score was stored in GameMode, a widget, or another non-replicated owner.
Score resets on death It lived only on the pawn that death destroyed.
End screen appears only for the host The host created local UI instead of clients reacting to replicated state or an owning-client message.
Score doubles Death/objective completion ran from more than one path without a one-shot gate.
Objective UI says complete, but the door stays locked Widget state changed instead of the objective owner or server rule state.
Player joins late and sees no objective state Objective was only sent as an event, not stored as replicated current state.
High score disappears after restarting the game It never got copied into a SaveGame object and saved to a slot.
Win/loss booleans contradict each other Several booleans modeled one phase instead of a single enum.
Client can cheat score Client-side UI or pawn graph directly changed authoritative score.

Debug by printing the actor name, net mode, authority, active GameMode class, GameState class, PlayerState class, old score, new score, and match phase. If the print happened on the wrong machine, fix ownership before changing math.

The pattern everyone actually uses

For a first enemy score loop:

BP_Enemy HealthComponent OnDeath
-> BP_Enemy BeginDeath runs once
-> BP_Enemy reports death to GameMode or objective manager with KillerController

GameMode AwardEnemyDeath
-> validate score should count
-> KillerController Get PlayerState
-> BP_MatchPlayerState.Score += EnemyScoreValue
-> GameState.TeamScore += optional team points
-> CheckForEndCondition

HUD / scoreboard
-> reads local PlayerState for personal score
-> reads GameState and PlayerArray for match/team/scoreboard rows

For a first objective:

Generator repaired on server
-> Generator broadcasts OnRepaired once
-> BP_ObjectiveManager updates CompletedCount
-> BP_ObjectiveManager writes public summary to GameState
-> if complete: GameMode CheckForEndCondition
-> HUD refreshes objective text from GameState

Lookalikes - which one do I want?

Owner Use when Not for
GameMode Server rules: award score, start/end match, decide win/loss, respawn rules. Client-readable HUD state or durable saves.
GameState Public match state: phase, timer, team score, objective summary, winner. Private player input or per-player score rows.
PlayerState Public per-player score, team, name, ready, kills/deaths, state through respawn. Body-specific health or private settings.
Objective / wave manager actor Level-specific coordination of placed generators, doors, spawners, waves, checkpoints. Whole-project global state or unrelated systems.
PlayerController / HUD Local UI creation, input buttons, owner-only presentation, restart/menu requests. The authoritative score or objective truth.
Pawn / Character Current body health, movement, weapons, hit reactions, body-specific death. Score that survives death.
GameInstance Temporary local data across map travel. Replicated match state or saved-to-disk progress.
SaveGame Durable records that survive quitting. Live actors, widgets, timers, or current replicated truth.

Rule of thumb: GameMode decides, GameState announces match state, PlayerState announces player state, managers coordinate local objectives, widgets present, and SaveGame remembers after the live world is gone.

Going deeper