Construction Script vs BeginPlay¶
Construction Script prepares an actor. BeginPlay starts gameplay for that actor.
The one-minute version¶
- Construction Script is actor setup. It runs while an actor is being constructed: when you place or edit an actor in the editor, and when a new actor is spawned at runtime.
- BeginPlay is gameplay start. It runs when the actor enters an actual play world and the actor is ready for gameplay logic.
- Editing an instance property in the Details panel can rerun Construction Script in the editor. It does not rerun BeginPlay.
- A spawned actor runs construction as part of spawning. If the world is already playing, BeginPlay follows after the spawn/initialization path.
- Use Construction Script for editor-visible setup: choose meshes, set relative transforms, preview a fence made from repeated components, apply instance defaults.
- Use BeginPlay for runtime behavior: find gameplay references, bind events, start timers, spawn gameplay actors, read player state, and call networked logic.
What each one actually is¶
Both live on an Actor Blueprint, but they answer different questions.
flowchart TD
Defaults[Class defaults and components] --> Construct[Construction Script]
Construct --> Prepared[Prepared actor instance]
Prepared --> Play[BeginPlay]
Play --> Gameplay[Runtime gameplay logic]
Edit[Edit property in editor] -. reruns .-> Construct
Spawn[Spawn Actor from Class] --> Construct
Construction Script is Unreal saying: "Build this actor instance from its
class, defaults, components, and per-instance editable values." In C++ terms,
it is part of the actor construction path around ExecuteConstruction,
UserConstructionScript, and OnConstruction.
For placed actors, that work happens in the editor as you create and edit the instance. When you press Play in Editor (PIE), Unreal duplicates or loads the prepared actor into the play world and then begins gameplay. Do not assume the Construction Script reruns at play start for every placed actor.
For spawned actors, construction happens during the spawn path. That is why Spawn Actor from Class can expose variables as spawn pins: those values are assigned before the spawned actor's Construction Script finishes.
BeginPlay is later. It is the actor's first gameplay event for that play life. Components have been initialized, the world is ticking or about to tick, and runtime-only systems such as timers, overlaps, controllers, and other actors are now the right things to talk to.
Why editing a property reruns one but not the other¶
Construction Script is editor tooling as much as runtime setup. If you make an
instance-editable variable like NumberOfFencePosts and change it in the
Details panel, Unreal reruns construction so the viewport can show the new
fence immediately.
That rerun is useful, but it is also the trap:
- it can happen many times while you drag, compile, duplicate, or change properties;
- script-created components may be rebuilt;
- references you cached from the previous construction pass can become stale;
- side effects outside the actor are not automatically undone.
BeginPlay is not editor preview. It belongs to a play session. Changing a property in the editor does not fire BeginPlay because no gameplay has started.
What belongs in Construction Script¶
Use Construction Script when the result should be visible and repeatable before the game runs.
Good examples:
- set a mesh or material from an instance-editable variable;
- position child components based on a length or count;
- generate a preview-only layout from components owned by the actor;
- clamp or normalize editable setup values;
- copy an Expose on Spawn value into component setup for a spawned actor.
The rule is: running it twice should not create two lasting gameplay side effects. It should rebuild the same actor setup from the current inputs.
The C++ shape is OnConstruction:
void ATrainingDoor::OnConstruction(const FTransform& Transform)
{
Super::OnConstruction(Transform);
DoorMesh->SetRelativeScale3D(FVector(1.0f, DoorWidthScale, 1.0f));
TriggerBox->SetBoxExtent(FVector(80.0f, DoorWidthScale * 50.0f, 120.0f));
}
What belongs in BeginPlay¶
Use BeginPlay when the logic depends on the game actually running.
Good examples:
- cache the player, controller, GameMode, or another runtime actor;
- bind to an Event Dispatcher;
- start a timer;
- spawn a projectile, enemy, pickup, or effect actor;
- initialize health, ammo, score, or save-loaded state;
- run server-authoritative setup in a networked game.
BeginPlay runs once for that actor's current play lifetime. If the actor is destroyed and a new actor is spawned, the new actor gets its own BeginPlay.
The C++ shape is:
void ATrainingDoor::BeginPlay()
{
Super::BeginPlay();
CachedPlayer = UGameplayStatics::GetPlayerCharacter(this, 0);
GetWorldTimerManager().SetTimer(CheckTimer, this,
&ATrainingDoor::CheckForPlayer, 0.25f, true);
}
The classic mistakes¶
- Spawning gameplay actors in Construction Script. The editor may rerun construction repeatedly, and a spawned external actor is not just "part of this actor's preview." Use components or a Child Actor Component for preview structure; use BeginPlay or an explicit gameplay event for gameplay spawns.
- Finding the player in Construction Script. In the editor there may be no real player. During runtime spawn, possession and player setup may not be the thing you think it is yet. Use BeginPlay, possession events, or an explicit reference passed on spawn.
- Randomizing permanent setup in Construction Script. If construction reruns, the result changes again. Use a saved seed or move the random gameplay choice to BeginPlay.
- Assuming BeginPlay means every other actor is ready. BeginPlay is the first good runtime hook, not a global "all actors are initialized in the order I imagined" guarantee. If actor A depends on actor B, pass the reference, bind an event, or use a short timer/retry only when the dependency is genuinely asynchronous.
- Setting a normal variable after Spawn Actor and expecting Construction Script to see it. Use an Expose on Spawn variable, or manual deferred spawning, when the construction step needs the value.
Lookalikes - which one do I want?¶
| Hook or tool | Runs when | Use when |
|---|---|---|
| Construction Script | Actor is constructed or rerun in the editor; spawned actor is constructed at runtime | You need repeatable setup visible on the actor before play. |
| BeginPlay | Actor begins actual gameplay | You need runtime references, timers, spawns, input-adjacent setup, or networked gameplay state. |
| Class Defaults | Before any instance exists | You want the default value every new instance starts with. |
| Expose on Spawn | Value is supplied during spawn before construction finishes | A spawned actor's construction needs caller-provided setup. |
| PostInitializeComponents | C++ initialization after components initialize, before BeginPlay | You are writing native code and need a pre-BeginPlay component hook. |
| OnPossessed / Possessed | A controller takes control of a pawn | Pawn setup depends on who controls it, not merely that play began. |
Rule of thumb: if you want the editor viewport to show it, think Construction Script. If you want the running game to do it, think BeginPlay or a later gameplay event.
Going deeper¶
- Spawn Actor from Class - how Expose on Spawn and deferred spawning fit into construction timing.
- The frame: Tick, timers, and timelines - why timers and per-frame gameplay belong after play starts.
- Get Player Character / Pawn / Controller - why player lookups belong in gameplay timing, not editor construction.
- Ownership and lifetime - why externally spawned actors are not automatically owned, attached, or destroyed with the actor that created them.
- Official docs: AActor, Actor Lifecycle, AActor::UserConstructionScript, AActor::OnConstruction, Event BeginPlay, and AActor::BeginPlay.
- Engine source (requires engine access - we do not reproduce it here):
AActor::ExecuteConstruction,AActor::ProcessUserConstructionScript, andAActor::RerunConstructionScriptsinEngine/Source/Runtime/Engine/Private/ActorConstruction.cpp;AActor::BeginPlayandAActor::DispatchBeginPlayinEngine/Source/Runtime/Engine/Private/Actor.cpp.