npx skills add ...
npx skills add quodsoler/unreal-engine-skills --skill ue-actor-component-architecture
Use when writing or debugging AActor and UActorComponent C++: constructor vs BeginPlay, PostInitializeComponents, EndPlay cleanup, SpawnActor/SpawnActorDeferred/FinishSpawning, CreateDefaultSubobject, NewObject + RegisterComponent, SetupAttachment/AttachToComponent, tick groups, UChildActorComponent, UINTERFACE, actor pooling. Also use when the user mentions 'BeginPlay', 'Tick', 'SpawnActor', 'CreateDefaultSubobject', 'attach a component', 'root component', 'lifecycle', 'PrimaryActorTick', 'FAttachmentTransformRules'. For collision and overlaps, see ue-physics-collision; for GameMode/Pawn/Controller, see ue-gameplay-framework; for replicated actors, see ue-networking-replication.
npx skills add quodsoler/unreal-engine-skills --skill 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".
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 |
| Creating, attaching, activating components | Component System |
| Overriding component hooks (OnRegister, InitializeComponent, TickComponent) | Component Lifecycle Hooks |
| SpawnActor, deferred spawn, pooling | Spawning |
| Tick groups, tick interval, prerequisites, tick vs timer | Ticking |
| Cross-actor capability without casting | Interfaces |
| Splitting a fat actor into components, data-driven loadouts | Composition Patterns |
| Full event order, EEndPlayReason, network and child-actor timing | references/actor-lifecycle.md |
| Which built-in component to use, header paths, member APIs | references/component-types.md |
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).
Order from the AActor class comment (Actor.h:243-278); full table with citations in references/actor-lifecycle.md.
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).
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.
| 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.
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.
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().
| 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.
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).
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.
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.
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.
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.
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.
| 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 |
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:
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.
| Do not emit | Use in 5.8 | Source |
|---|---|---|
FindComponentByInterface<UMyInterface>() | FindComponentByInterface<IMyInterface>() | UE_DEPRECATED(5.5) in GameFramework/Actor.h:3844 |
PreDuplicate(FObjectDuplicationParameters&) | PreDuplicateFromRoot(FObjectDuplicationParameters& DupParams) | UE_DEPRECATED(5.6) in GameFramework/Actor.h:2413 |
Actor->LastRenderTime | GetLastRenderTime() / SetLastRenderTime(float) | UE_DEPRECATED(5.6) in GameFramework/Actor.h:138 |
NetUpdateFrequency = 30.f, MinNetUpdateFrequency = ..., NetCullDistanceSquared = ... | SetNetUpdateFrequency(), SetMinNetUpdateFrequency(), SetNetCullDistanceSquared() | UE_DEPRECATED(5.5) in GameFramework/Actor.h:898-909 |
BeginReplication() / EndReplication() overrides | OnReplicationStartedForIris / OnStopReplicationForIris / FillReplicationParams; see ue-networking-replication | UE_DEPRECATED(5.7) in GameFramework/Actor.h:3493-3506 |
APawn::GetMovementBase() | GetMovementBaseObject() / GetMovementBaseInterfaceData(); see ue-character-movement | UE_DEPRECATED(5.8) in GameFramework/Pawn.h:59 |
AttachTo(...), K2_AttachTo(...), AttachRootComponentTo(...) | AttachToComponent(Parent, FAttachmentTransformRules, SocketName) | UE_DEPRECATED(4.17) in GameFramework/Actor.h:1991, Components/SceneComponent.h:740 |
AttachRootComponentToActor(...) | AttachToActor(ParentActor, FAttachmentTransformRules, SocketName) | UE_DEPRECATED(4.17) in GameFramework/Actor.h:2018 |
DetachFromParent(bool), DetachRootComponentFromParent(bool) | DetachFromComponent(FDetachmentTransformRules), DetachFromActor(FDetachmentTransformRules) | UE_DEPRECATED(4.12) in Components/SceneComponent.h:768; UE_DEPRECATED(4.17) in GameFramework/Actor.h:2045 |
Comp->bReplicates = true | SetIsReplicatedByDefault(true) (constructor) or SetIsReplicated(true) | private member, Components/ActorComponent.h:264-267 |
Comp->bCreatedByConstructionScript | Comp->CreationMethod == EComponentCreationMethod::UserConstructionScript or IsCreatedByConstructionScript() | @deprecated Replaced by CreationMethod, Components/ActorComponent.h:306-310 |
SceneComp->RelativeLocation = ..., SceneComp->bVisible = ..., SceneComp->AttachParent | SetRelativeLocation(), SetVisibility(), GetAttachParent() | private members, Components/SceneComponent.h:109-183 |
Actor->bHidden | IsHidden() / SetActorHiddenInGame(bool) | private member, GameFramework/Actor.h:365 |
GetComponentsByClass(...) in C++ (the C++ name is K2_GetComponentsByClass) | GetComponents<T>(OutArray) or TInlineComponentArray<T*> | "intended to only be used by blueprints", GameFramework/Actor.h:3801-3807 |
LightmapType on primitives | GetLightmapType() / SetLightmapType() | UE_DEPRECATED(5.5) in Components/PrimitiveComponent.h:359 |
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.
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 travelAActor::AActor() CDO + every instance. No world. CreateDefaultSubobject, tick config, defaults
UObject::PostLoad() level-placed actors only (not spawned)
UActorComponent::OnComponentCreated() native components on spawn (not when loaded from a level)
AActor::PreRegisterAllComponents() → RegisterComponent() per component → AActor::PostRegisterAllComponents()
AActor::PostActorCreated() spawned actors only, before construction scripts
UserConstructionScript() → AActor::OnConstruction(const FTransform&)
AActor::PreInitializeComponents() gameplay only
UActorComponent::Activate() if bAutoActivate
UActorComponent::InitializeComponent() if bWantsInitializeComponent, once per session
AActor::PostInitializeComponents() components initialized; bind to own components here
AActor::BeginPlay() level is ticking; may be delayed for networked or child actors
AActor::Tick(float DeltaSeconds) if PrimaryActorTick.bCanEverTick
AActor::EndPlay(const EEndPlayReason::Type) base implementation forwards EndPlay to components
AActor::Destroyed() explicit Destroy() only; not on level streaming or game end// MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
class UStaticMeshComponent;
class UMyHealthComponent;
UCLASS()
class MYGAME_API AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
protected:
virtual void PostInitializeComponents() override;
virtual void BeginPlay() override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UFUNCTION()
void HandleDeath();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components")
TObjectPtr<UStaticMeshComponent> MeshComp;
UPROPERTY(VisibleAnywhere, Category="Components")
TObjectPtr<UMyHealthComponent> HealthComp;
};// MyActor.cpp
#include "MyActor.h"
#include "Components/StaticMeshComponent.h"
#include "TimerManager.h"
#include "MyHealthComponent.h"
AMyActor::AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = false;
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
SetRootComponent(MeshComp); // SetupAttachment(MeshComp) parents a scene component here; no world needed
HealthComp = CreateDefaultSubobject<UMyHealthComponent>(TEXT("Health")); // logic-only: no attachment
}
void AMyActor::PostInitializeComponents()
{
Super::PostInitializeComponents();
HealthComp->OnDeath.AddDynamic(this, &AMyActor::HandleDeath); // own components are initialized
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
SetActorTickEnabled(true); // gameplay starts here
}
void AMyActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
GetWorldTimerManager().ClearAllTimersForObject(this);
Super::EndPlay(EndPlayReason); // base forwards EndPlay to components
}
void AMyActor::HandleDeath() { Destroy(); }// MyHealthComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MyHealthComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMyOnDeathSignature);
UCLASS(ClassGroup=(MyGame), meta=(BlueprintSpawnableComponent))
class MYGAME_API UMyHealthComponent : public UActorComponent
{
GENERATED_BODY()
public:
UMyHealthComponent();
UPROPERTY(BlueprintAssignable, Category="Health")
FMyOnDeathSignature OnDeath;
UFUNCTION(BlueprintCallable, Category="Health")
void ApplyDamage(float Amount);
protected:
virtual void InitializeComponent() override;
UPROPERTY(EditAnywhere, Category="Health")
float MaxHealth = 100.f;
UPROPERTY(VisibleInstanceOnly, Transient, Category="Health")
float CurrentHealth = 0.f;
};// MyHealthComponent.cpp
#include "MyHealthComponent.h"
UMyHealthComponent::UMyHealthComponent()
{
PrimaryComponentTick.bCanEverTick = false; // event-driven: no per-frame work
bWantsInitializeComponent = true; // opt in to InitializeComponent()
}
void UMyHealthComponent::InitializeComponent()
{
Super::InitializeComponent();
CurrentHealth = MaxHealth; // once per gameplay session, before BeginPlay
}
void UMyHealthComponent::ApplyDamage(float Amount)
{
CurrentHealth = FMath::Max(0.f, CurrentHealth - Amount);
if (CurrentHealth <= 0.f) { OnDeath.Broadcast(); }
}// In AMyActor (declared: void AddRuntimeLight();)
#include "Components/PointLightComponent.h"
void AMyActor::AddRuntimeLight()
{
// NewObject creates the UObject only; RegisterComponent creates render/physics state and enables tick.
UPointLightComponent* Light = NewObject<UPointLightComponent>(this, TEXT("RuntimeLight"));
Light->SetupAttachment(GetRootComponent()); // allowed before RegisterComponent
Light->RegisterComponent();
AddInstanceComponent(Light); // adds to InstanceComponents: visible in Details, serialized per instance
Light->SetIntensity(5000.f);
}void AMyCharacter::EquipWeapon(UStaticMeshComponent* WeaponMesh)
{
WeaponMesh->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, TEXT("WeaponSocket"));
}// AMyEnemySpawner member: UPROPERTY(EditDefaultsOnly) TSubclassOf<AMyEnemy> EnemyClass;
AMyEnemy* AMyEnemySpawner::SpawnEnemy(const FVector& Location, const FRotator& Rotation)
{
FActorSpawnParameters Params;
Params.Owner = this;
Params.Instigator = GetInstigator();
Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
return GetWorld()->SpawnActor<AMyEnemy>(EnemyClass, Location, Rotation, Params);
}// AMyEnemy declares: void SetEnemyData(UMyEnemyData* Data);
AMyEnemy* AMyEnemySpawner::SpawnEnemyFromData(const FTransform& SpawnTransform, UMyEnemyData* Data)
{
AMyEnemy* Enemy = GetWorld()->SpawnActorDeferred<AMyEnemy>(EnemyClass, SpawnTransform, this, GetInstigator(),
ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
if (!Enemy) { return nullptr; }
Enemy->SetEnemyData(Data); // runs before OnConstruction and BeginPlay
Enemy->FinishSpawning(SpawnTransform, false, nullptr, ESpawnActorScaleMethod::MultiplyWithRoot);
return Enemy;
}AMyEnemy::AMyEnemy()
{
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = false; // later: SetActorTickEnabled(true)
PrimaryActorTick.TickInterval = 0.1f; // later: SetActorTickInterval(0.1f)
PrimaryActorTick.TickGroup = TG_PostPhysics;
}// MyInteractable.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "MyInteractable.generated.h"
UINTERFACE(MinimalAPI, Blueprintable)
class UMyInteractable : public UInterface
{
GENERATED_BODY()
};
class MYGAME_API IMyInteractable
{
GENERATED_BODY()
public:
// C++ default + Blueprint override; UHT also generates Execute_OnInteract(UObject*, AActor*) for safe calls
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="Interaction")
void OnInteract(AActor* InstigatorActor);
};// Caller (AMyCharacter, declared: void TryInteract(AActor* Target);)
void AMyCharacter::TryInteract(AActor* Target)
{
if (Target && Target->Implements<UMyInteractable>()) // U-class for the reflection check
{
IMyInteractable::Execute_OnInteract(Target, this); // works for C++ and Blueprint implementers
}
// Components implementing the interface: FindComponentByInterface<IMyInteractable>() takes the I-class (Actor.h:3852)
}void AMyEnemy::ApplyLoadout(const UMyEnemyData* Data)
{
for (const TSubclassOf<UActorComponent>& CompClass : Data->ExtraComponents)
{
UActorComponent* Comp = NewObject<UActorComponent>(this, CompClass);
if (!Comp) { continue; }
Comp->RegisterComponent(); // starts ticking and registers with the world
AddInstanceComponent(Comp); // shows up in the details panel and is saved with the actor
}
}