An Actor (AActor) is anything you can place or spawn in a level. Components are the
reusable pieces of behavior/representation you compose onto actors. "Composition over
inheritance" is the intended design: prefer adding components over deep actor class hierarchies.
When to use this skill
- Creating a new
AActororUActorComponentsubclass. - Setting up a component hierarchy (root + attached scene components) and choosing the root.
- Spawning actors (immediate or deferred) or adding/registering components at runtime.
- Wiring overlap/hit events, or deciding what runs in the constructor vs
BeginPlay. - Debugging "BeginPlay didn't run", "component has no transform / doesn't render", attachment problems, or "my runtime component does nothing".
Component types
UActorComponent is the base for all components. The three tiers, in increasing capability:
| Type | Has transform? | Renders/collides? | Use for |
|---|---|---|---|
UActorComponent |
no | no | pure behavior/data (health, inventory, AI logic, abilities) |
USceneComponent |
yes (location/rotation/scale) | no | transform nodes, attach points, spring arms, cameras |
UPrimitiveComponent |
yes | yes | meshes, collision, anything drawn or physical |
Concrete primitives you will use most: UStaticMeshComponent, USkeletalMeshComponent,
UCapsuleComponent, UBoxComponent, USphereComponent, UCameraComponent,
USpringArmComponent.
Key consequences of the hierarchy:
- Only
USceneComponent-derived components have a transform, can be attached into a hierarchy, or be the root component. - Only
USceneComponent/UPrimitiveComponentcreate a render state by default; plainUActorComponents don't (nothing to draw). - Only
UPrimitiveComponentcreates a physics state by default (collision/simulation).
See references/components-and-registration.md for the full type breakdown, render/physics state, and registration internals.
AActor lifecycle (order matters)
The canonical order for a typical gameplay actor:
- Constructor — set defaults,
CreateDefaultSubobjectfor owned components. No world, no gameplay; also runs on the Class Default Object (CDO) and in the editor. OnConstruction(Transform)— re-runs whenever a placed actor's properties change (construction script). Runs in editor and on spawn; keep it idempotent.PreInitializeComponents→ per-componentInitializeComponent→PostInitializeComponents— components exist & are registered; safe to wire them together.BeginPlay— gameplay starts. Do gameplay init here (spawning, timers, delegate bindings), not in the constructor.Tick(DeltaSeconds)— per-frame, only if ticking is enabled (see Ticking).EndPlay(Reason)— leaving play (destroyed, level change, PIE end, app shutdown); clean up timers/delegates here.Destroyed(legacy) thenBeginDestroy/FinishDestroyduring garbage collection.
There are three distinct creation paths that converge before BeginPlay: load-from-disk
(PostLoad), Play-in-Editor duplication (PostDuplicate), and spawning (PostActorCreated →
OnConstruction). PostLoad and PostActorCreated are mutually exclusive. The full flow,
including deferred spawn and the GC sequence, is in
references/actor-lifecycle.md.
Verified in 5.8 (GameFramework/Actor.h): BeginPlay():2125, EndPlay():2132,
PostInitProperties():2343, Tick(float):3060, PreInitializeComponents():3124,
PostInitializeComponents():3127, OnConstruction():3445, Destroyed():3569.
Constructor vs BeginPlay: the constructor runs on the CDO and in the editor, with no world.
Never do gameplay logic (spawning, world queries, timers, delegate binding) there — use BeginPlay.
Authoring an actor with components
// Pickup.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Pickup.generated.h"
class USphereComponent;
class UStaticMeshComponent;
UCLASS()
class MYGAME_API APickup : public AActor
{
GENERATED_BODY()
public:
APickup();
protected:
virtual void BeginPlay() override;
// Overlap handlers must be UFUNCTION() with the exact delegate signature, or AddDynamic fails.
UFUNCTION()
void OnOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& Sweep);
UPROPERTY(VisibleAnywhere) TObjectPtr<USphereComponent> Trigger; // root, collision
UPROPERTY(VisibleAnywhere) TObjectPtr<UStaticMeshComponent> Mesh; // visual
};
// Pickup.cpp
#include "Pickup.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
APickup::APickup()
{
PrimaryActorTick.bCanEverTick = false; // default OFF; opt in only if you override Tick
Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("Trigger"));
SetRootComponent(Trigger);
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
Mesh->SetupAttachment(Trigger); // attach in the constructor with SetupAttachment
}
void APickup::BeginPlay()
{
Super::BeginPlay();
Trigger->OnComponentBeginOverlap.AddDynamic(this, &APickup::OnOverlap);
}
Key rules:
CreateDefaultSubobject<T>(TEXT("UniqueName"))is constructor-only; the names must be unique within the actor. It is declared onUObject(CoreUObject/Public/UObject/Object.h).- Hold component pointers in
UPROPERTY() TObjectPtr<T>members so the GC keeps them alive and they show in the editor. A rawT*UPROPERTY also works butTObjectPtris the modern form. - Set the root with
SetRootComponent(or assignRootComponent). The actor's world transform comes from its root. - In the constructor, attach with
SetupAttachment(Parent). At runtime, useAttachToComponent. - Forward-declare component classes in the header and
#includethe concrete component headers in the.cppto keep header dependencies light.
Spawning actors at runtime
FActorSpawnParameters Params;
Params.Owner = this;
Params.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
APickup* P = GetWorld()->SpawnActor<APickup>(PickupClass, Location, Rotation, Params);
// Deferred spawn: set properties/expose-on-spawn values before BeginPlay runs
AThing* T = GetWorld()->SpawnActorDeferred<AThing>(ThingClass, Transform);
T->Damage = 50.f;
T->FinishSpawning(Transform); // runs construction → PostInitializeComponents → BeginPlay
PickupClassis typically aUPROPERTY(EditAnywhere) TSubclassOf<APickup>so designers pick the Blueprint subclass. Spawning the C++ class directly skips Blueprint-authored defaults/components.- Use deferred spawn when the actor needs values set before
BeginPlay(e.g. a projectile's damage/owner). PlainSpawnActorruns the constructor andBeginPlayimmediately. GetWorld()can be null on the CDO/in the constructor — only spawn from gameplay code.
Full spawn-parameter fields, collision-handling values, and destroy/pooling guidance: references/spawning-and-destroying.md.
Adding components at runtime
UStaticMeshComponent* Extra = NewObject<UStaticMeshComponent>(this);
Extra->SetupAttachment(GetRootComponent()); // or AttachToComponent if already registered
Extra->RegisterComponent(); // REQUIRED so it ticks/renders/collides
Runtime components must be RegisterComponent()-ed — registration is what associates a
component with the world/scene so it can update, render, and collide. Default subobjects created in
the constructor are registered for you during the actor's spawn. Registering many components during
play has a cost; prefer creating them as default subobjects when you can.
Attachment
// Runtime attach/detach (scene components only):
Mesh->AttachToComponent(Target, FAttachmentTransformRules::SnapToTargetIncludingScale, SocketName);
Mesh->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
// Whole-actor attach (attaches this actor's root to another actor/component):
AttachToActor(OtherActor, FAttachmentTransformRules::KeepRelativeTransform);
SetupAttachmentis for the constructor / not-yet-registered components;AttachToComponentattaches immediately and is for play. UsingSetupAttachmentat runtime does nothing without registration.- Attachment rules choose, per channel, whether to keep the world transform or snap to the parent/socket. A component can have many children but only one parent; cycles are not allowed.
Attachment rules, sockets, mobility, and relative-vs-world transforms: references/attachment-and-transforms.md.
Ticking
- Actors: set
PrimaryActorTick.bCanEverTick = true;in the constructor, then overrideTick. - Components: set
PrimaryComponentTick.bCanEverTick = true;then overrideTickComponent. - Both default to off.
bCanEverTickonly makes ticking possible; you can toggle it at runtime withPrimaryActorTick.SetTickFunctionEnable(true/false). - Prefer events/timers (
timers-and-async) over ticking when you can — ticking everything is a common performance sink. LeavebCanEverTick = falsefor actors that don't need per-frame work.
Finding components
UStaticMeshComponent* M = GetComponentByClass<UStaticMeshComponent>(); // first of class
TArray<USceneComponent*> All;
GetComponents<USceneComponent>(All); // all of class
Gotchas
- Gameplay logic in the constructor — runs on the CDO/editor with no world; use
BeginPlay. - Overlap/hit handler not a
UFUNCTION()—AddDynamicsilently fails to fire; the bound function must be aUFUNCTION()with the exact delegate signature. - Forgot
RegisterComponent()on a runtime component → it won't render/collide/tick. - Attaching a non-scene component — only
USceneComponent+ can attach or have a transform. SetupAttachmentat runtime does nothing without registration; useAttachToComponent.- No overlaps firing — the primitive needs collision enabled and
SetGenerateOverlapEvents(true)on both components, with overlapping collision responses. - No cleanup in
EndPlay— timers/delegates referencing this actor can dangle; clear them.EndPlayruns for all exit reasons, so it's the right place (notDestroyed). - Static mobility moved at runtime — only
Movablecomponents can be transformed during play; setting transform on aStaticcomponent is ignored/asserts. - Spawning into a blocked location can fail and return null; set a collision-handling override.
Version notes
TObjectPtr<T>is the current idiom for object UPROPERTYs (UE5+); older code uses rawT*, which still works. Seememory-and-gc.- The lifecycle callbacks and component model here are stable across UE5; line numbers in citations drift between patch releases, but the header paths and class/function names are stable.
References & source material
Engine source (UE 5.8, under Engine/Source/Runtime/):
Engine/Classes/GameFramework/Actor.h—AActorlifecycle,RootComponent:1024,PrimaryActorTick:318,SetRootComponent:2493,AttachToActor:2029,FinishSpawning:3117,GetComponentByClass:3796.Engine/Classes/Components/ActorComponent.h—UActorComponent,RegisterComponent:1322,OnRegister:830,InitializeComponent:919,BeginPlay:936,TickComponent:976,PrimaryComponentTick:177,bWantsInitializeComponent:340.Engine/Classes/Components/SceneComponent.h— transforms,SetupAttachment:734,AttachToComponent:752,DetachFromComponent:786,Mobility:303.Engine/Classes/Components/PrimitiveComponent.h— rendering/collision,OnComponentBeginOverlap:1468,SetGenerateOverlapEvents:418,SetCollisionEnabled:2026.Engine/Classes/Engine/World.h—SpawnActor/SpawnActorDeferred:3851,FActorSpawnParameters:420.Engine/Classes/Engine/EngineTypes.h—FAttachmentTransformRules:75,EEndPlayReason:3670,ESpawnActorCollisionHandlingMethod:4411.CoreUObject/Public/UObject/Object.h—CreateDefaultSubobject:151.
Official docs (UE 5.8):
- Actor Lifecycle — https://dev.epicgames.com/documentation/unreal-engine/unreal-engine-actor-lifecycle
- Components — https://dev.epicgames.com/documentation/unreal-engine/components-in-unreal-engine
- Actors — https://dev.epicgames.com/documentation/unreal-engine/actors-in-unreal-engine
- Spawning and Destroying an Actor — https://dev.epicgames.com/documentation/unreal-engine/spawning-and-destroying-unreal-engine-actors
- Actor Ticking — https://dev.epicgames.com/documentation/unreal-engine/actor-ticking-in-unreal-engine
Deep-dive references in this skill:
- references/actor-lifecycle.md — full creation paths, component init sub-sequence, end-of-life and garbage collection.
- references/components-and-registration.md — type
hierarchy, registration/render/physics state,
InitializeComponentvsBeginPlay, ticking. - references/attachment-and-transforms.md — attachment APIs, transform rules, sockets, mobility.
- references/spawning-and-destroying.md — spawn variants, spawn parameters, deferred spawn, destruction, pooling.