ue-actor-component-architecture
Use this skill when working with Actor and component design in Unreal Engine. Triggers on: Actor, component, BeginPlay, Tick, SpawnActor, lifecycle, CreateDefaultSubobject, composition, EndPlay, PostInitializeComponents, UActorComponent, USceneComponent, UINTERFACE, attachment, spawn, interface. See
By quodsoler · 870 installs
npx skills add quodsoler/unreal-engine-skills --skill ue-actor-component-architecture
Source repository · Upstream listing
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.
Request is about… Go to
Where code belongs: constructor, PostInitializeComponents, BeginPlay, EndPlay [Actor Lifecycle]( actor lifecycle)
Creating, attaching, activating components [Component System]( component system)
Overriding component hooks (OnRegister, InitializeComponent, TickComponent) [Component Lifecycle Hooks]( component lifecycle hooks)
SpawnActor, deferred spawn, pooling [Spawning]( spawning)
Tick groups, tick interval, prerequisites, tick vs timer [Ticking]( ticking)
Cross actor capability without casting [Interfaces]( interfaces)
Splitting a fat actor into components, data driven loadouts [Composition Patterns]( composition patterns)
Full event order, EEndPlayReason, network and child actor timing [references/actor lifecycle.md](references/actor lifecycle.md)
Which built in component to use, header paths, member APIs [references/component types.md](references/component types.md)
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](references/actor lifecycle.md).
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
Class Transform Render/Collision Use for
UActorComponent No No Pure logic and data: health, inventory, cooldowns
USceneComponent Yes No Anchors, pivots, grouping, attach points
UPrimitiveComponent Yes Yes Meshes, shapes, anything visible or collidable
Built in subclasses, header paths and member APIs: [references/component types.md](references/component types.md).
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
Call Where Header
SetupAttachment(USceneComponent InParent, FName InSocketName = NAME None) Constructor, or any unregistered component SceneComponent.h:734
AttachToComponent(USceneComponent InParent, const FAttachmentTransformRules& AttachmentRules, FName InSocketName = NAME None) Runtime; valid registered or not, but prefer SetupAttachment before registration. Returns bool (false = rejected, parent unchanged) SceneComponent.h:752
DetachFromComponent(const FDetachmentTransformRules& DetachmentRules) Runtime SceneComponent.h:786
AActor::AttachToComponent(...) , AttachToActor(AActor ParentActor, const FAttachmentTransformRules&, FName SocketName = NAME None) , DetachFromActor(const FDetachmentTransformRules&) Attach the whole actor via its root; both attach calls return bool Actor.h:2015, 2029, 2062
K2 AttachToComponent(...) , K2 AttachToActor(...) Blueprint nodes only; C++ calls the non K2 forms Actor.h:2006, 2042
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 .
Hook Line Fires
virtual void OnComponentCreated() :1331 Native components when the actor is spawned; not for components loaded from a level
virtual void OnRegister() / virtual void OnUnregister() :830, :835 Around world registration; render/physics state is created after OnRegister
virtual void InitializeComponent() / virtual void UninitializeComponent() :919, :955 Only when bWantsInitializeComponent ( :340 ) is true; once per session
virtual void BeginPlay() / virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) :936, :949 Play start/stop
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction ThisTickFunction) :976 Requires PrimaryComponentTick.bCanEverTick and registration
virtual void OnComponentDestroyed(bool bDestroyingHierarchy) :1338 From DestroyComponent
HasBeenCreated() , HasBeenInitialized() , HasBegunPlay() , IsRegistered() , IsBeingDestroyed() :505 520, :1316 State queries; the bit fields are private
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](references/component types.md actor pooling).
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 ).
ETickingGroup ( EngineBaseTypes.h:83 109 ) Use
TG PrePhysics Default. Input, movement requests, AI decisions
TG DuringPhysics Work that does not read this frame's physics results
TG PostPhysics Camera, IK, anything reading final physics transforms
TG PostUpdateWork Last gameplay group
TG StartPhysics , TG EndPhysics , TG LastDemotable , TG NewlySpawned Engine internal; never assign
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
Need Use instead
Delayed or repeating work GetWorldTimerManager().SetTimer(FTimerHandle& InOutHandle, UserClass InObj, TMethodPtr InTimerMethod, float InRate, bool InbLoop = false, float InFirstDelay = 1.f) ( TimerManager.h:167 )
React to state change Dynamic multicast delegate on the component, bound in PostInitializeComponents
Collision OnComponentBeginOverlap , OnComponentHit ( PrimitiveComponent.h:1457 1475 ), OnActorBeginOverlap ( Actor.h:1368 ); see ue physics collision
Cheap per frame updates Keep tick but throttle with TickInterval and disable with SetActorTickEnabled(false) when idle
Interfaces
Use a UINTERFACE for