UE Actor-Component Architecture
Target engine: UE 5.8. APIs below are verified against the 5.8 headers; older forms are listed under "Deprecated — do not use".
Build actors out of components: lifecycle hooks in the right order, spawning (immediate, deferred, pooled), component creation and attachment, ticking, and C++ interfaces. Everything here is in the Engine module (GameFramework/Actor.h, Components/ActorComponent.h, Components/SceneComponent.h, Engine/World.h) on top of CoreUObject; a game module's Build.cs needs "Core", "CoreUObject", "Engine".
Context
Read .agents/ue-project-context.md if it exists (module names, conventions, enabled plugins, GAS/networking setup). Do not stop if it is missing.
Identify the area from the request and the codebase. Ask only when two plausible readings would produce different code.
Mental Model
Never new/delete an actor or component. Actors: UWorld::SpawnActor and AActor::Destroy(bool bNetForce = false, bool bShouldModifyLevel = true) (Actor.h:2327). Components: CreateDefaultSubobject/NewObject and UActorComponent::DestroyComponent(bool bPromoteChildren = false) (ActorComponent.h:1328).
Actor Lifecycle
Order from the AActor class comment (Actor.h:243-278); full table with citations in references/actor-lifecycle.md [blocked].
Constructor vs BeginPlay
The constructor runs on the Class Default Object, where GetWorld() is nullptr. Anything that needs the world or other actors goes in PostInitializeComponents (own components) or BeginPlay (gameplay).
EEndPlayReason::Type (Engine/EngineTypes.h:3670-3685): Destroyed, LevelTransition, EndPlayInEditor, RemovedFromWorld, Quit. Clear timers and unbind delegates for every reason, then call Super::EndPlay(EndPlayReason).
Network note
On clients, BeginPlay "can be delayed for networked or child actors" (Actor.h:275). Property arrival hooks are PreNetReceive()/PostNetReceive() (Actor.h:2943-2946) and PostNetInit(), "always called immediately after spawning and reading in replicated properties" (Actor.h:2958). Replication rules, OnRep_ functions and RPCs are owned by ue-networking-replication.
Component System
Built-in subclasses, header paths and member APIs: references/component-types.md [blocked].
Writing a component
Component replication: bReplicates is private on UActorComponent (ActorComponent.h:267); call SetIsReplicatedByDefault(true) in the constructor or SetIsReplicated(true) later, and the owning actor must replicate. Details in ue-networking-replication.
Creating components at runtime
UnregisterComponent() removes world presence but keeps the object (reversible); DestroyComponent() unregisters and marks it for GC. A component whose Outer is an actor is added to that actor's OwnedComponents (ActorComponent.cpp:600 in PostInitProperties, :951 on register), which AActor::AddReferencedObjects reports to GC (Actor.cpp:574), so it stays alive until DestroyComponent() or the actor dies, with or without a UPROPERTY. Still hold your own pointer in a UPROPERTY (TObjectPtr) so it is tracked and nulled safely; a raw pointer dangles after DestroyComponent().
Attachment
FAttachmentTransformRules presets (Engine/EngineTypes.h:78-81): KeepRelativeTransform, KeepWorldTransform, SnapToTargetNotIncludingScale, SnapToTargetIncludingScale; per-axis constructor FAttachmentTransformRules(EAttachmentRule InLocationRule, EAttachmentRule InRotationRule, EAttachmentRule InScaleRule, bool bInWeldSimulatedBodies). FDetachmentTransformRules presets (:125-126): KeepRelativeTransform, KeepWorldTransform.
Activation (ActorComponent.h)
uint8 bAutoActivate:1 is public (:317): set it in the constructor. Runtime: virtual void Activate(bool bReset = false) (:580), virtual void Deactivate() (:586), virtual void SetActive(bool bNewActive, bool bReset = false) (:594), virtual void ToggleActive() (:600), bool IsActive() const (:607; bIsActive is private). Gate activation by overriding virtual bool ShouldActivate() const (:807). Delegates OnComponentActivated / OnComponentDeactivated (:559, :563).
Component Lifecycle Hooks
Override these on UActorComponent subclasses; signatures are verbatim from Components/ActorComponent.h.
GetOwner() (:538) and GetOwner<T>() (:542) return the owning actor; GetWorld() is valid once registered.
Spawning
FActorSpawnParameters (Engine/World.h:420-519): Name, Template, Owner, Instigator, OverrideLevel, SpawnCollisionHandlingOverride, TransformScaleMethod, bNoFail, bDeferConstruction, bAllowDuringConstructionScript, NameMode, ObjectFlags, CustomPreSpawnInitialization. Collision handling values (Engine/EngineTypes.h:4411-4423): Undefined, AlwaysSpawn, AdjustIfPossibleButAlwaysSpawn, AdjustIfPossibleButDontSpawnIfColliding, DontSpawnIfColliding.
Deferred spawn: configure before construction and BeginPlay
SpawnActorDeferred<T>(UClass* Class, FTransform const& Transform, AActor* Owner = nullptr, APawn* Instigator = nullptr, ESpawnActorCollisionHandlingMethod CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::Undefined, ESpawnActorScaleMethod TransformScaleMethod = ESpawnActorScaleMethod::MultiplyWithRoot) (World.h:3851-3870) sets bDeferConstruction = true. Finish with FinishSpawning(const FTransform& Transform, bool bIsDefaultTransform = false, const FComponentInstanceDataCache* InstanceDataCache = nullptr, ESpawnActorScaleMethod TransformScaleMethod = ESpawnActorScaleMethod::OverrideRootScale) (Actor.h:3117). Until then the actor "is in a malformed state" (Actor.h:632).
The two defaults differ: SpawnActorDeferred uses MultiplyWithRoot, FinishSpawning uses OverrideRootScale. Pass the scale method explicitly when the root component has a non-unit default scale.
Object pooling
SpawnActor/Destroy churn for projectiles or casings costs GC time. Park actors instead: hide, disable collision and tick; reuse by reversing. AActor has no IsActive(); use IsHidden() (Actor.h:4523) as the parked flag.
Full pooled-actor class (Acquire/Release with GC-safe storage): references/component-types.md [blocked].
Ticking
FTickFunction fields (Engine/EngineBaseTypes.h): bCanEverTick (:213), bStartWithTickEnabled (:217), bTickEvenWhenPaused (:209), bAllowTickOnDedicatedServer (:221), TickGroup (:196), EndTickGroup (:204), TickInterval (:258). Actors use PrimaryActorTick (Actor.h:318), components PrimaryComponentTick (ActorComponent.h:177).
Ordering between specific objects: AddTickPrerequisiteActor(AActor* PrerequisiteActor) and AddTickPrerequisiteComponent(UActorComponent* PrerequisiteComponent) exist on both AActor (Actor.h:2093, 2097) and UActorComponent (ActorComponent.h:1355, 1359). SetTickableWhenPaused(bool bTickableWhenPaused) (Actor.h:2113, ActorComponent.h:618). Change group at runtime with SetTickGroup(ETickingGroup NewTickGroup) (Actor.h:3566, ActorComponent.h:1351). Fixed-step physics callbacks: bAsyncPhysicsTickEnabled (Actor.h:699) plus virtual void AsyncPhysicsTickActor(float DeltaTime, float SimTime) (Actor.h:2930); see ue-physics-collision.
When not to tick
Interfaces
Use a UINTERFACE for a capability ("can be interacted with") that unrelated actors share; use a component when the behavior owns state or needs to tick. Blueprint-facing exposure rules (BlueprintImplementableEvent, meta= specifiers, latent actions) are owned by ue-blueprint-cpp-interop.
Implementers inherit both classes, class MYGAME_API AMyChest : public AActor, public IMyInteractable, and override virtual void OnInteract_Implementation(AActor* InstigatorActor) override;. Callers never invoke OnInteract directly:
Composition Patterns
Prefer a flat actor plus components over deep hierarchies (AMyCharacter + UMyHealthComponent + UMyInventoryComponent), with one class and many data assets instead of one subclass per variant. Components talk through the owner (GetOwner()->FindComponentByClass<UMyHealthComponent>()) or through delegates, never through cached raw pointers to siblings. Data-driven loadouts add components at runtime from a TArray<TSubclassOf<UActorComponent>> on a data asset:
For components injected into actors by plugins or Game Features, use UGameFrameworkComponentManager::AddReceiver(AActor* Receiver, bool bAddOnlyInGameWorlds = true) from ModularGameplay (Beta in 5.8); see ue-game-features.
Deprecated — do not use
Common Mistakes
World access in the constructor: GetWorld() is nullptr on the CDO; SpawnActor, timers and lookups belong in BeginPlay, component binding in PostInitializeComponents.
Skipping Super::: every lifecycle override calls its Super:: (BeginPlay first thing; EndPlay(EndPlayReason) last). AActor::EndPlay is what forwards EndPlay to components; RouteEndPlay ensures against a missing Super::EndPlay (Actor.cpp:3230).
Pooled or cached actors outside a UPROPERTY: a plain TArray<AMyProjectile*> member is invisible to GC; declare UPROPERTY() TArray<TObjectPtr<AMyProjectile>>. For non-owning references in non-UObject code use TWeakObjectPtr<AActor> and check IsValid().
AttachToComponent in the constructor: the header allows it on unregistered components, but SetupAttachment is the intended call there (SceneComponent.h:745-746); use AttachToComponent from BeginPlay onward and check its bool result.
NewObject without RegisterComponent: the component never gets render or physics state and never ticks. Also call AddInstanceComponent if it should appear in the Details panel and survive serialization.
Deferred spawn without FinishSpawning: the actor stays in a malformed state (Actor.h:632); always pair SpawnActorDeferred with FinishSpawning.
Calling an interface method directly: Target->OnInteract(...) bypasses Blueprint implementers. Test with Implements<UMyInteractable>(), call through IMyInteractable::Execute_OnInteract(Target, ...).
Polling in Tick: if (HealthComp->IsDead()) every frame is replaced by binding OnDeath once; disable tick with SetActorTickEnabled(false) when nothing needs per-frame work.
Assuming child actors exist in PostInitializeComponents: UChildActorComponent children may begin play late (Actor.h:275); read GetChildActor() in or after BeginPlay.
Writing private component flags: bReplicates, bIsActive, bHasBegunPlay, RelativeLocation and bVisible are private; use the setters and queries listed in the tables above.
Related Skills
ue-cpp-foundations— UCLASS/UPROPERTY/UFUNCTION specifiers, TObjectPtr vs TWeakObjectPtr, subsystems, GC rulesue-gameplay-framework— GameMode, GameState, PlayerController, Pawn, Character, damage without GASue-physics-collision— collision channels and profiles, overlap and hit events, sweeps, async physics tickue-networking-replication— SetReplicates, property replication, OnRep, RPCs, subobject replication, Irisue-gameplay-cameras— USpringArmComponent, UCameraComponent, PlayerCameraManager, Gameplay Cameras pluginue-blueprint-cpp-interop— Blueprint-implementable interfaces, BlueprintNativeEvent rules, latent actions, meta specifiersue-character-movement— CharacterMovementComponent, movement base API, CMC vs Moverue-animation-system— USkeletalMeshComponent animation, AnimInstance, montagesue-audio-system— UAudioComponent playback, attenuation, MetaSoundsue-niagara-effects— UNiagaraComponent, system spawning, user parametersue-game-features— ModularGameplay components, UGameFrameworkComponentManager, Game Feature actionsue-gameplay-abilities— UAbilitySystemComponent placement on actorsue-state-trees— UStateTreeComponent on actors and AI controllersue-ai-navigation— perception and navigation componentsue-procedural-generation— UProceduralMeshComponent, UDynamicMeshComponent, instancing at scaleue-editor-tools— detail customizations, editor utility widgets, UToolMenus and editor subsystemsue-input-system— Enhanced Input actions, mapping contexts, triggers, modifiers and user settingsue-mass-entity— Mass processors, fragments, queries and entity traitsue-materials-rendering— material instances, parameter collections, render targets and post processue-sequencer-cinematics— Level Sequences, playback, cine cameras and Movie Render Graphue-world-level-streaming— World Partition, level streaming, data layers and travel


