Objective and Wave Manager Actors vs GameMode/GameState¶
A manager actor is a named coordinator in the level: "this arena wave," "this generator objective," "this checkpoint route." It is not a dumping ground for every global variable in the project.
The one-minute version¶
- Use a placed manager actor when the system coordinates placed actors in a level: generators, levers, doors, spawn points, race checkpoints, puzzle pieces, room enemies, reward spawns, or music/combat phases.
- Use GameMode when the system is a rule authority: can the match start, should score count, can the player respawn, did this objective end the game, which pawn class should spawn?
- Use GameState when the result is public runtime state: wave number, objective progress, team score, match phase, remaining enemies, current winner, or any value clients and HUDs should read.
- Use Level Blueprint for one-off map choreography, not reusable objective systems. If the graph has a name, state, reset logic, and several assigned references, it probably wants a manager actor.
- Managers should find participants deliberately: editable arrays, registration
events, gameplay tags, or a one-time BeginPlay search for small maps. Do not
hide repeated
Get All Actors Of Classcalls in Tick or widget bindings. - In multiplayer, the server owns the manager's gameplay decisions. Clients read replicated manager/GameState data and run local presentation.
What a manager actor is¶
A manager actor is just an Actor Blueprint with a coordination job.
BP_ArenaWaveManager placed in L_CaveArena
SpawnPoints: array of TargetPoint references
EnemyClasses: array of enemy class references
CurrentWave: integer
AliveEnemies: set of enemy actor references
OnWaveChanged dispatcher
It exists in the world, can be selected in the editor, can expose references in the Details panel, and can print/debug like any other actor. The value is not that it is magic. The value is that the coordination has one visible owner instead of being hidden in a map graph, widget, pawn, or GameMode variable pile.
Good manager jobs:
- "All three generators repaired opens the vault."
- "Spawn wave 4 when wave 3 enemies are dead."
- "Track race checkpoints for this route."
- "When this room puzzle completes, unlock this door and spawn this reward."
- "This boss arena changes phase when weak points are disabled."
Poor manager jobs:
- the player's movement or input graph;
- global save profile data;
- every objective in the entire game;
- all UI button logic;
- health math for every enemy;
- match rules clients need to read but the manager does not replicate.
The decision table¶
| Put it here | Use when | Warning sign |
|---|---|---|
| Level Blueprint | One map, one-off choreography, Sequencer, streaming trigger, a single placed reference script. | The graph is copied into another map or has a reusable system name. |
| Objective / wave manager actor | A named level system coordinates participants and may be reused as a class. | It starts owning unrelated systems or hidden global state. |
| Participant actor / component | A generator, door, checkpoint, enemy, or spawner owns its own local behavior. | A manager directly edits every tiny internal variable instead of calling clear functions/events. |
| GameMode | Server rules, scoring decisions, respawn, match start/end, objective completion meaning. | A client widget needs to read its variables. |
| GameState | Public match/objective summary everyone should read. | It starts holding editor-assigned references to every room object. |
| PlayerState | Per-player progress/score/team/ready state. | The value belongs to the room, team, or match instead of one player. |
| GameInstance / SaveGame | Cross-level temporary data or durable completed-objective records. | You store live actor references from the current map. |
The manager is the middle layer: close enough to the map to own placed references, but named and reusable enough to avoid a giant Level Blueprint.
Example: generator objective¶
Each generator owns being repaired:
BP_Generator
bRepaired
OnRepaired dispatcher
Repair() function
The manager owns the group rule:
BP_GeneratorObjectiveManager
Generators: array of BP_Generator references
RequiredCount: integer
CompletedCount: integer
VaultDoor: BP_Door reference
BeginPlay:
for each Generator:
bind Generator.OnRepaired to HandleGeneratorRepaired
RefreshObjectiveSummary
HandleGeneratorRepaired(Generator):
if Generator already counted:
return
mark counted
CompletedCount += 1
RefreshObjectiveSummary
if CompletedCount >= RequiredCount:
CompleteObjectiveOnce()
Completion can then touch the wider framework deliberately:
CompleteObjectiveOnce:
if bCompleted:
return
bCompleted = true
VaultDoor.OpenDoor()
GameState.ObjectiveSummary = "Generators complete"
GameMode.ObjectiveCompleted(self)
The generator does not know the whole mission. The door does not know the score rules. The manager coordinates the room. GameMode decides what the completion means for the game.
Example: wave manager¶
A wave manager is useful because spawn points, enemy classes, wave state, and enemy-death subscriptions all belong to this arena.
BP_WaveManager
WaveDefinitions: array of FWaveDefinition
SpawnPoints: array of TargetPoint references
CurrentWaveIndex
AliveEnemies: set of Actor references
bWaveActive
StartNextWave:
CurrentWaveIndex += 1
for each enemy entry in WaveDefinitions[CurrentWaveIndex]:
choose SpawnPoint
Spawn Actor from Class
bind enemy OnDeath to HandleEnemyDied
add enemy to AliveEnemies
GameState.WaveNumber = CurrentWaveIndex + 1
GameState.RemainingEnemies = AliveEnemies length
HandleEnemyDied(Enemy):
remove Enemy from AliveEnemies
GameState.RemainingEnemies = AliveEnemies length
if AliveEnemies is empty:
FinishWaveOnce()
If wave completion awards score or ends the match, GameMode should validate and apply that rule. If the HUD displays wave and enemy count, GameState should hold the public values or the manager itself must replicate those values clearly.
How managers find participants¶
Use the least surprising reference strategy that fits the level.
| Strategy | Use when | Caveat |
|---|---|---|
| Editable references in Details | A designer can assign the exact doors, levers, generators, or spawn points. | Missing assignments produce None; validate on BeginPlay. |
| Participants register with manager | Actors spawn dynamically or stream in and can find the manager once. | Needs unregister on EndPlay and duplicate guards. |
| Gameplay tags or component tags | The manager needs "all generators tagged Objective.A" in one area. | Tags classify; they do not replace validation or ownership. |
| One-time BeginPlay search | A small map has a few known actors and no streaming complication. | Do not repeat the search every frame or from widgets. |
| Expose on Spawn | A spawner creates participants and passes the manager reference immediately. | Spawned actors still need to handle manager destruction/endplay. |
Validate early:
BeginPlay:
if Generators is empty:
Print warning "GeneratorObjective has no generators"
for each Generator:
if not Is Valid:
Print warning with manager name and missing index
A variable typed BP_Door is not an assigned door. The manager still needs a
real actor reference.
Communication shape¶
Use functions for commands and dispatchers for announcements:
Manager -> Door:
OpenDoor()
Generator -> Manager:
OnRepaired broadcast
Enemy -> Manager:
OnDeath broadcast
Manager -> UI/GameState:
update public ObjectiveSummary
broadcast OnObjectiveChanged locally if useful
Avoid making the manager reach through every participant's internals:
Manager:
Generator.bRepaired = true
Generator.Mesh.SetMaterial(...)
Generator.Audio.Stop()
Prefer:
Manager:
Generator.SetRepairedByObjective()
The participant owns its own presentation and local state. The manager owns the group rule.
Reset and replay¶
Objectives and waves often need a reset path for death, retry, checkpoint load, level restart, or editor testing.
Write a real reset function:
ResetObjective:
bCompleted = false
CompletedCount = 0
CountedGenerators empty
for each Generator:
Generator.ResetGenerator()
VaultDoor.CloseDoor()
RefreshObjectiveSummary()
Do not rely on Open Level as your only reset if the game supports retrying a
room, respawning in place, or streaming the area without full travel.
For a wave manager:
ResetWaves:
destroy or despawn live enemies owned by this manager
clear AliveEnemies
CurrentWaveIndex = -1
bWaveActive = false
RefreshObjectiveSummary()
The manager should also clean up event bindings and live references in EndPlay when it has bound to long-lived actors or systems.
Save and load¶
Save plain objective facts, not live objects:
FSavedObjectiveState
ObjectiveId
bCompleted
CompletedParticipantIds
CurrentWaveIndex
RemainingWaveSeedOrCount
On load:
Manager BeginPlay
-> ObjectiveId matches saved record
-> assign current-map participants by ID/reference setup
-> apply saved completed state
-> rebuild public GameState summary
Do not save actor references, timer handles, widget references, or spawned enemy object pointers. Those belong to the old world. Store stable IDs and rebuild current-world references after the map loads.
Replication¶
In a networked game, a manager actor is still an actor. If clients need to see its state directly, the manager must replicate and its variables must be replicated deliberately. Many beginner projects are cleaner if the manager updates GameState for public HUD facts:
Server BP_WaveManager:
owns AliveEnemies and spawn decisions
updates BP_MatchGameState.WaveNumber
updates BP_MatchGameState.RemainingEnemies
Clients:
read GameState values
update HUD, music, markers, and local presentation
Use replicated manager state when the manager itself is a relevant gameplay actor clients inspect, such as a capture zone, payload, or objective marker. Use GameState when the value is a match-wide summary.
Rules that prevent most replication bugs:
- spawn gameplay enemies and objective actors on the server when everyone should see them;
- let clients request interaction through an owned server path;
- change objective truth on the server;
- replicate current state for late joiners, not only past events;
- do not create widgets inside GameMode or replicated managers for remote clients;
- print authority and net mode from the manager while debugging.
Avoid the hidden god Blueprint¶
A manager actor should have one named responsibility.
Good names:
BP_ArenaWaveManagerBP_GeneratorObjectiveManagerBP_RaceCheckpointManagerBP_RoomPuzzleManagerBP_BossPhaseManager
Warning names:
BP_GameManagerBP_LevelManagerBP_EverythingControllerBP_GlobalLogic
The name does not prove the design is wrong, but it is a useful smell. If the manager handles waves, inventory, pause menu, save slots, player input, UI, enemy health, weather, and level travel, it is no longer a manager for one objective. Split it by owner and audience.
Failure modes¶
| Symptom | Likely cause |
|---|---|
| Objective never completes | A participant reference is unassigned, registered too late, or never fired its dispatcher. |
| Completion runs twice | The manager lacks a bCompleted or counted-participant gate. |
| Works in one copied map but not another | Level Blueprint or manager instance references still point at the old map assumptions. |
| HUD shows stale progress | UI cached a value instead of reading manager/GameState updates. |
| Client sees a wave number but enemies do not exist | Enemies were spawned only on a client, or replicated actors are not relevant/spawned by server. |
| Late-joining client sees no objective | Current objective state was only broadcast as an event, not stored as replicated state. |
| Reset leaves old enemies or bindings | Reset cleared counters but did not clean live actors, timers, or dispatcher bindings. |
| Save/load points at destroyed actors | Save data stored actor references instead of stable IDs/plain values. |
| Manager becomes impossible to debug | It owns unrelated global systems instead of one named coordination job. |
Print manager name, objective ID, authority, participant count, completed count, current phase, and bound actor names. Most failures are missing references, duplicate events, wrong machine, or state living only in UI.
The pattern everyone actually uses¶
Room objective:
BP_RoomPuzzleManager placed in the level
-> editable array of BP_Lever references
-> editable BP_Door reference
-> BeginPlay binds each Lever.OnStateChanged
-> HandleLeverChanged recomputes "all active"
-> CompleteObjectiveOnce opens door and updates GameState
-> GameMode receives ObjectiveCompleted if this affects score/win/loss
Wave arena:
BP_WaveManager placed in arena
-> editable SpawnPoints and WaveDefinitions
-> StartWave spawns server-owned enemies
-> each enemy OnDeath removes it from AliveEnemies
-> GameState.WaveNumber / RemainingEnemies refresh
-> wave complete starts delay or asks GameMode to end/check score
Race checkpoints:
BP_RaceCheckpointManager
-> ordered checkpoint actors register or are assigned
-> PlayerState stores current checkpoint index per player if public
-> GameState stores race phase / winner / public timer
-> GameMode validates finish and awards result
Lookalikes - which one do I want?¶
| Tool | Use when | It does not |
|---|---|---|
| Level Blueprint | One-off map sequence or placed actor glue that will not be reused. | Create a reusable class or clean manager API. |
| Objective / wave manager actor | A named level system coordinates participants and exposes designer references. | Replace GameMode, GameState, PlayerState, SaveGame, or every actor's own behavior. |
| GameMode | Server rule authority: scoring, win/loss, start/end, respawn. | Replicate variables to remote clients. |
| GameState | Public match/objective summary clients and UI should read. | Hold every placed actor reference or run all local room logic. |
| Actor Component | The same participant behavior belongs on many actor classes. | Coordinate a whole room by itself unless that is truly its job. |
| Blueprint Interface | Participants expose a shared contract such as ResetObjectiveParticipant. |
Store shared state or broadcast changes by itself. |
| Event Dispatcher | Participants announce repair/death/activated state to the manager. | Find references or guarantee lifetime without binding/unbinding. |
| Get All Actors Of Class | One-time discovery for small maps or editor tools. | Replace assigned references, registration, or per-frame state ownership. |
Rule of thumb: participants own their local behavior, managers coordinate the level objective, GameMode decides game rules, GameState exposes public state, and UI presents what those owners already know.
Going deeper¶
- Score, objectives, and win/loss state - where score, phase, objective progress, UI, and SaveGame results belong.
- Level Blueprint vs Actor Blueprint vs Manager Actors - the broader ownership decision before this objective-specific pattern.
- GameMode & friends, single-player edition - default classes, spawn flow, and GameMode's rule-owner role.
- GameState and PlayerState in practice - public replicated match and player state.
- Blueprint variables and references - typed variables, editable instance references, and why assignment still matters.
- Event Dispatchers - participant-to-manager notifications and binding lifecycle.
- Get Actor Of Class / Get All Actors Of Class - why repeated world searches are usually missing ownership.
- Save Game: Create / Save / Load Game to Slot - saving objective IDs and plain progress instead of actor references.
- Replication basics - server-spawned actors, replicated variables, and late-join current state.
- Official docs: Level Blueprint, Introduction to Blueprints, and Game Mode and Game State.
- Engine source (requires engine access - we do not reproduce it here):
ALevelScriptActordeclarations inEngine/Source/Runtime/Engine/Classes/Engine/LevelScriptActor.hand implementation inEngine/Source/Runtime/Engine/Private/LevelScriptActor.cpp; actor lifecycle and dispatch/endplay behavior inEngine/Source/Runtime/Engine/Private/Actor.cpp; GameMode/GameState framework ownership inEngine/Source/Runtime/Engine/Private/GameModeBase.cppandEngine/Source/Runtime/Engine/Private/GameStateBase.cpp.