Skip to content

Actors, components, subsystems

Three categories cover almost everything you will write in this codebase. Pick the right one and the rest of the engine works with you.

Actor (AActor)

A thing that can be placed in a level. Has a transform, a lifecycle, can tick. Subclass when the thing is a thing — a rune, a slot, a calculator, a clock.

Lifecycle:

  • Constructor — runs on the CDO and on every instance. Construct subobjects (CreateDefaultSubobject), set defaults. Do not access the world, other actors, or anything outside the actor itself. The world doesn't exist yet.
  • PostInitializeComponents — components are constructed but BeginPlay hasn't fired. Rare to override.
  • BeginPlay — called once when play starts (in editor PIE, on level load, on actor spawn). Bind delegates, query the world, find other actors, schedule timers.
  • Tick — every frame, if PrimaryActorTick.bCanEverTick = true. Default is off in this project unless a class needs it.
  • EndPlay — actor is being destroyed or the world is tearing down. Unbind delegates, cancel timers.

Examples in the codebase: ARune, ARuneSlot, ARuneCalculator, AClockDevice, ARephraseCharacter, AMemoryPalace.

ActorComponent (UActorComponent)

Something attached to an actor. Adds behavior or state without forcing inheritance. Subclass when:

  • Multiple unrelated actors need the same behavior (URuneCarryComponent on the player; could go on any actor).
  • A behavior should attach and detach at runtime (URuneEffectComponent subclasses — added by a strategy when a verb fires, removed when the sentence breaks).

Lifecycle is similar to actors but parallel: BeginPlay, TickComponent, EndPlay. Components can also be USceneComponent subclasses if they need a transform (the spring arm, mesh component, fire spawn point).

Examples in the codebase: URuneCarryComponent, every URuneEffectComponent subclass, UFireSpawnPointComponent.

Subsystem

A singleton scoped to a particular owner. The engine creates exactly one per owner; you grab it with a static accessor. Use when something is global within its scope and shouldn't be tied to any actor's lifetime.

Four scopes, only the first two used here:

Subsystem Lives as long as Used in this project for
UGameInstanceSubsystem Game instance (across level loads) USentenceValidator, UEffectExecutor, URulePriorityManager, URuneEventBus, URuneRedefinitionManager
UWorldSubsystem The current world (per-level) URuneableObjectRegistry, URuneEffectHistory, URuneCameraSubsystem
UEngineSubsystem Whole engine not used
UEditorSubsystem The editor not used

Auto-create. No Spawn, no NewObject. The engine instantiates them when their owner exists.

Get one:

UGameInstance* GI = GetWorld()->GetGameInstance();
USentenceValidator* Validator = GI->GetSubsystem<USentenceValidator>();

URuneableObjectRegistry* Reg = GetWorld()->GetSubsystem<URuneableObjectRegistry>();

Override Initialize(FSubsystemCollectionBase& Collection) and Deinitialize() for setup and teardown. To add a new subsystem, see Adding a subsystem.

Decision: actor vs component vs subsystem

Question If yes
Is the thing visible in the level and placed by a designer? Actor
Is the thing a behavior that attaches to one or more actors? Component
Is the thing global (single, lookup-style, shared by everyone)? Subsystem
Does it need to outlive levels? GameInstanceSubsystem
Should it reset per level? WorldSubsystem

A common mistake: making something an actor because you want to place it once and reference it everywhere. That's a subsystem. Actors should be in the level because they belong in the level.

Tick

Tick/TickComponent is your per-frame hook. Off by default for components, on by default for some actor types. Toggle in the constructor:

PrimaryActorTick.bCanEverTick = true;     // actor
PrimaryComponentTick.bCanEverTick = true; // component

Then later:

SetActorTickEnabled(true);  // or false

If you don't need to run every frame, don't tick. Use FTimerHandle and GetWorldTimerManager().SetTimer(...) for delayed or repeating logic instead.

Constructor caveat

Code that runs in the constructor runs once on the CDO at module init, plus once per spawned instance. If you do anything that depends on the world there, the CDO run will crash.

AMyActor::AMyActor()
{
    GetWorld()->SpawnActor<...>(...);  // BUG: CDO has no world
}

Move world-touching code to BeginPlay. The constructor is for setting defaults, creating components (CreateDefaultSubobject), and configuring static fields.

Source files

  • Source/Rephrased_Demo/SentenceValidator.hUGameInstanceSubsystem example
  • Source/Rephrased_Demo/RuneSystem/Subsystems/RuneableObjectRegistry.hUWorldSubsystem example
  • Source/Rephrased_Demo/RuneCarryComponent.hUActorComponent example
  • Source/Rephrased_Demo/RephraseCharacter.h — typical actor with multiple subobjects

Change history

  • 2026-05-23 — Gabriel Li — Moved under Unreal basics track (skippable for experienced UE engineers).
  • 2026-05-08 — Gabriel Li — Added baseline change history section for weekly wiki maintenance.