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 butBeginPlayhasn'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, ifPrimaryActorTick.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 (
URuneCarryComponenton the player; could go on any actor). - A behavior should attach and detach at runtime (
URuneEffectComponentsubclasses — 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:
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.
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.h—UGameInstanceSubsystemexampleSource/Rephrased_Demo/RuneSystem/Subsystems/RuneableObjectRegistry.h—UWorldSubsystemexampleSource/Rephrased_Demo/RuneCarryComponent.h—UActorComponentexampleSource/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.