mcpField
U
ue-actor-component-architecture

ue-actor-component-architecture

github.com/quodsoler開発4191.2k 回インストール4 週間前に更新
リポジトリ

ue-actor-component-architecture について

UE 5.8 の C++ でコンポーネントから Actor を構成し、そのライフサイクルを扱うためのコーディングスキルです。コンポーネントの作成とアタッチ、Actor のスポーン、Tick、インターフェースを解説します。

主な機能

  1. 01Actor とコンポーネントのライフサイクル
  2. 02コンポーネントの作成、登録、アタッチ
  3. 03即時および遅延 Actor スポーン
  4. 04Tick の設定とスケジューリング
  5. 05C++ インターフェースとコンポジションパターン

活用シーン

  1. 01Actor のロジックを置く場所の判断
  2. 02実行時のコンポーネント追加とアタッチ
  3. 03構築前の設定を伴う Actor のスポーン
  4. 04Actor の機能をコンポーネントに分割

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/quodsoler/unreal-engine-skills --skill ue-actor-component-architecture

インストール後、Claude Code で/ue-actor-component-architectureを入力して読み込まれたか確認します。

同梱ファイルreferences/actor-lifecycle.mdreferences/component-types.md

詳細

UE Actor-Component アーキテクチャ

対象エンジン:UE 5.8。以下の API は 5.8 のヘッダーに照らして検証済みです。古い形式は「非推奨 — 使用しないでください」に記載しています。

コンポーネントを組み合わせてアクターを構築します。ライフサイクルフックの正しい順序、スポーン(即時、遅延、プール)、コンポーネントの作成とアタッチ、ティック、C++ インターフェースを扱います。ここで取り上げる内容はすべて、CoreUObject を基盤とする Engine モジュール(GameFramework/Actor.h、Components/ActorComponent.h、Components/SceneComponent.h、Engine/World.h)に含まれます。ゲームモジュールの Build.cs には "Core", "CoreUObject", "Engine" が必要です。

コンテキスト

.agents/ue-project-context.md が存在する場合は読み込んでください(モジュール名、規約、有効なプラグイン、GAS/ネットワーク設定)。ファイルがなくても作業を中断しないでください。

リクエストとコードベースから対象領域を特定してください。妥当な解釈が 2 通りあり、それぞれで異なるコードになる場合に限り、質問してください。

リクエストの内容…参照先
コードを記述する場所:コンストラクター、PostInitializeComponents、BeginPlay、EndPlayアクターのライフサイクル
コンポーネントの作成、アタッチ、有効化コンポーネントシステム
コンポーネントフックのオーバーライド(OnRegister、InitializeComponent、TickComponent)コンポーネントのライフサイクルフック
SpawnActor、遅延スポーン、プーリングスポーン
ティックグループ、ティック間隔、前提条件、ティックとタイマーの使い分けティック
キャストを使わずにアクター間で機能を提供する方法インターフェース
肥大化したアクターのコンポーネントへの分割、データ駆動型ロードアウトコンポジションパターン
イベントの全順序、EEndPlayReason、ネットワークと子アクターのタイミングreferences/actor-lifecycle.md
使用するビルトインコンポーネント、ヘッダーパス、メンバー APIreferences/component-types.md

概念モデル

UObject
  └── AActor                            placeable/spawnable; owns components      GameFramework/Actor.h
        └── UActorComponent             logic only, no transform                  Components/ActorComponent.h
              └── USceneComponent       transform + attachment tree               Components/SceneComponent.h
                    └── UPrimitiveComponent  rendering + collision + physics      Components/PrimitiveComponent.h

アクターやコンポーネントに対して、new/delete を決して使わないでください。アクターには UWorld::SpawnActor と AActor::Destroy(bool bNetForce = false, bool bShouldModifyLevel = true)(Actor.h:2327)を使用します。コンポーネントには CreateDefaultSubobject/NewObject と UActorComponent::DestroyComponent(bool bPromoteChildren = false)(ActorComponent.h:1328)を使用します。

アクターのライフサイクル

AActor クラスコメント(Actor.h:243-278)に記載されている順序です。引用付きの全表は references/actor-lifecycle.md を参照してください。

AActor::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
コンストラクターと BeginPlay の使い分け

コンストラクターは、GetWorld() が nullptr となる Class Default Object 上で実行されます。ワールドや他のアクターを必要とする処理は、PostInitializeComponents(自身のコンポーネント向け)または BeginPlay(ゲームプレイ向け)に記述してください。

// 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(); }

EEndPlayReason::Type(Engine/EngineTypes.h:3670-3685)には、Destroyed、LevelTransition、EndPlayInEditor、RemovedFromWorld、Quit があります。すべての理由に対してタイマーをクリアし、デリゲートのバインドを解除してから、Super::EndPlay(EndPlayReason) を呼び出してください。

ネットワークに関する注意

クライアントでは、BeginPlay が「ネットワークアクターまたは子アクターの場合、遅延することがあります」(Actor.h:275)。プロパティ到着時のフックは PreNetReceive()/PostNetReceive()(Actor.h:2943-2946)と PostNetInit() です。PostNetInit() は「スポーンおよびレプリケートされたプロパティの読み込み直後に必ず呼び出されます」(Actor.h:2958)。レプリケーションルール、OnRep_ 関数、RPC は ue-networking-replication の対象です。

コンポーネントシステム

クラストランスフォームレンダリング/コリジョン用途
UActorComponentなしなしヘルス、インベントリ、クールダウンなどのロジックとデータのみ
USceneComponentありなしアンカー、ピボット、グループ化、アタッチポイント
UPrimitiveComponentありありメッシュ、形状、可視またはコリジョン可能なもの

ビルトインサブクラス、ヘッダーパス、メンバー API については、references/component-types.md を参照してください。

コンポーネントの作成
// 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(); }
}

コンポーネントのレプリケーション:UActorComponent では bReplicates は private です(ActorComponent.h:267)。コンストラクターで SetIsReplicatedByDefault(true) を呼び出すか、後から SetIsReplicated(true) を呼び出してください。また、所有するアクターもレプリケーションする必要があります。詳細は ue-networking-replication を参照してください。

実行時のコンポーネント作成
// 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);
}

UnregisterComponent() はワールド上から取り除きますが、オブジェクトは保持します(元に戻せます)。DestroyComponent() は登録を解除し、GC の対象にします。Outer がアクターであるコンポーネントは、そのアクターの OwnedComponents に追加されます(PostInitProperties 内の ActorComponent.cpp:600、登録時の :951)。この OwnedComponents は AActor::AddReferencedObjects によって GC に通知されます(Actor.cpp:574)。そのため、DestroyComponent() が呼ばれるかアクターが破棄されるまで、UPROPERTY の有無にかかわらずコンポーネントは存続します。それでも、追跡されて安全に null 化されるよう、自身のポインターを UPROPERTY(TObjectPtr)で保持してください。生ポインターは DestroyComponent() の後にダングリングポインターになります。

アタッチ
呼び出し使用場所ヘッダー
SetupAttachment(USceneComponent* InParent, FName InSocketName = NAME_None)コンストラクター、または未登録のコンポーネントSceneComponent.h:734
AttachToComponent(USceneComponent* InParent, const FAttachmentTransformRules& AttachmentRules, FName InSocketName = NAME_None)実行時。有効な登録済みコンポーネントでも未登録のコンポーネントでも使用できますが、登録前に SetupAttachment を使うことを推奨します。bool を返します(false = 拒否され、親は変更されません)SceneComponent.h:752
DetachFromComponent(const FDetachmentTransformRules& DetachmentRules)実行時SceneComponent.h:786
AActor::AttachToComponent(...)、AttachToActor(AActor* ParentActor, const FAttachmentTransformRules&, FName SocketName = NAME_None)、DetachFromActor(const FDetachmentTransformRules&)ルートを介してアクター全体をアタッチします。アタッチ呼び出しはいずれも bool を返しますActor.h:2015, 2029, 2062
K2_AttachToComponent(...)、K2_AttachToActor(...)Blueprint ノード専用です。C++ では K2_ のない形式を呼び出しますActor.h:2006, 2042

FAttachmentTransformRules のプリセット(Engine/EngineTypes.h:78-81):KeepRelativeTransform、KeepWorldTransform、SnapToTargetNotIncludingScale、SnapToTargetIncludingScale。軸ごとに指定するコンストラクターは FAttachmentTransformRules(EAttachmentRule InLocationRule, EAttachmentRule InRotationRule, EAttachmentRule InScaleRule, bool bInWeldSimulatedBodies) です。FDetachmentTransformRules のプリセット(:125-126):KeepRelativeTransform、KeepWorldTransform。

void AMyCharacter::EquipWeapon(UStaticMeshComponent* WeaponMesh)
{
    WeaponMesh->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, TEXT("WeaponSocket"));
}
アクティベーション(ActorComponent.h)

uint8 bAutoActivate:1 は public です(:317)。コンストラクターで設定してください。実行時に使用するもの: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 は private です)。virtual bool ShouldActivate() const(:807)をオーバーライドすると、アクティベーションを制御できます。デリゲートは OnComponentActivated / OnComponentDeactivated です(:559, :563)。

コンポーネントのライフサイクルフック

UActorComponent のサブクラスで以下をオーバーライドします。シグネチャは Components/ActorComponent.h の記述そのままです。

フック行呼び出されるタイミング
virtual void OnComponentCreated():1331アクターのスポーン時にネイティブコンポーネントで呼び出されます。レベルからロードされたコンポーネントでは呼び出されません
virtual void OnRegister() / virtual void OnUnregister():830, :835ワールドへの登録時と登録解除時に呼び出されます。描画/物理状態は OnRegister の後に作成されます
virtual void InitializeComponent() / virtual void UninitializeComponent():919, :955bWantsInitializeComponent(:340)が true の場合のみ、セッションごとに 1 回呼び出されます
virtual void BeginPlay() / virtual void EndPlay(const EEndPlayReason::Type EndPlayReason):936, :949プレイの開始/停止時
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction):976PrimaryComponentTick.bCanEverTick が有効で、コンポーネントが登録されている必要があります
virtual void OnComponentDestroyed(bool bDestroyingHierarchy):1338DestroyComponent から呼び出されます
HasBeenCreated(), HasBeenInitialized(), HasBegunPlay(), IsRegistered(), IsBeingDestroyed():505-520, :1316状態を確認します。ビットフィールドは private です

GetOwner()(:538)と GetOwner<T>()(:542)は、所有元のアクターを返します。GetWorld() は登録後に有効になります。

スポーン

FActorSpawnParameters(Engine/World.h:420-519):Name、Template、Owner、Instigator、OverrideLevel、SpawnCollisionHandlingOverride、TransformScaleMethod、bNoFail、bDeferConstruction、bAllowDuringConstructionScript、NameMode、ObjectFlags、CustomPreSpawnInitialization。衝突処理の値(Engine/EngineTypes.h:4411-4423):Undefined、AlwaysSpawn、AdjustIfPossibleButAlwaysSpawn、AdjustIfPossibleButDontSpawnIfColliding、DontSpawnIfColliding。

// 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);
}
遅延スポーン:構築と 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)は、bDeferConstruction = true に設定します。FinishSpawning(const FTransform& Transform, bool bIsDefaultTransform = false, const FComponentInstanceDataCache* InstanceDataCache = nullptr, ESpawnActorScaleMethod TransformScaleMethod = ESpawnActorScaleMethod::OverrideRootScale)(Actor.h:3117)で完了します。それまでは、アクターは「不正な状態です」(Actor.h:632)。

// 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;
}

2 つのデフォルト値は異なります。SpawnActorDeferred は MultiplyWithRoot を使用し、FinishSpawning は OverrideRootScale を使用します。ルートコンポーネントのデフォルトスケールが 1 以外の場合は、スケールメソッドを明示的に渡してください。

オブジェクトプーリング

プロジェクタイルや薬莢に対する SpawnActor/Destroy の繰り返しは、GC の処理時間を消費します。アクターを破棄せずに待機させます。非表示にし、衝突判定とティックを無効にして、再利用時に元に戻します。AActor に IsActive() はありません。待機中かどうかのフラグには IsHidden()(Actor.h:4523)を使用します。

プールされたアクターのクラス全体(GC セーフなストレージを使った Acquire/Release):references/component-types.md。

Tick

FTickFunction のフィールド(Engine/EngineBaseTypes.h):bCanEverTick(:213)、bStartWithTickEnabled(:217)、bTickEvenWhenPaused(:209)、bAllowTickOnDedicatedServer(:221)、TickGroup(:196)、EndTickGroup(:204)、TickInterval(:258)。アクターでは PrimaryActorTick(Actor.h:318)、コンポーネントでは PrimaryComponentTick(ActorComponent.h:177)を使用します。

AMyEnemy::AMyEnemy()
{
    PrimaryActorTick.bCanEverTick = true;
    PrimaryActorTick.bStartWithTickEnabled = false;   // later: SetActorTickEnabled(true)
    PrimaryActorTick.TickInterval = 0.1f;             // later: SetActorTickInterval(0.1f)
    PrimaryActorTick.TickGroup = TG_PostPhysics;
}
ETickingGroup(EngineBaseTypes.h:83-109)用途
TG_PrePhysicsデフォルト。入力、移動要求、AI の判断
TG_DuringPhysicsこのフレームの物理演算結果を参照しない処理
TG_PostPhysicsカメラ、IK、最終的な物理演算のトランスフォームを参照する処理
TG_PostUpdateWork最後のゲームプレイグループ
TG_StartPhysics、TG_EndPhysics、TG_LastDemotable、TG_NewlySpawnedエンジン内部用。割り当てないでください

特定のオブジェクト間の順序制御には、AActor(Actor.h:2093, 2097)と UActorComponent(ActorComponent.h:1355, 1359)の両方にある AddTickPrerequisiteActor(AActor* PrerequisiteActor) と AddTickPrerequisiteComponent(UActorComponent* PrerequisiteComponent) を使用します。SetTickableWhenPaused(bool bTickableWhenPaused)(Actor.h:2113、ActorComponent.h:618)。実行時にグループを変更するには SetTickGroup(ETickingGroup NewTickGroup)(Actor.h:3566、ActorComponent.h:1351)を使用します。固定ステップ物理コールバック:bAsyncPhysicsTickEnabled(Actor.h:699)と virtual void AsyncPhysicsTickActor(float DeltaTime, float SimTime)(Actor.h:2930)。ue-physics-collision を参照してください。

Tick を使わない場合
必要な処理代わりに使うもの
遅延または繰り返し処理GetWorldTimerManager().SetTimer(FTimerHandle& InOutHandle, UserClass* InObj, TMethodPtr InTimerMethod, float InRate, bool InbLoop = false, float InFirstDelay = -1.f)(TimerManager.h:167)
状態変化への対応コンポーネントの動的マルチキャストデリゲートを PostInitializeComponents でバインドします
衝突OnComponentBeginOverlap、OnComponentHit(PrimitiveComponent.h:1457-1475)、OnActorBeginOverlap(Actor.h:1368)。ue-physics-collision を参照してください
軽量なフレームごとの更新Tick は維持しつつ、TickInterval で頻度を抑え、アイドル時は SetActorTickEnabled(false) で無効にします

インターフェース

関連のないアクター間で共有する機能(「インタラクション可能」など)には UINTERFACE を使います。動作が状態を持つ場合や Tick が必要な場合は、コンポーネントを使います。Blueprint への公開ルール(BlueprintImplementableEvent、meta= 指定子、潜在アクション)については ue-blueprint-cpp-interop が担当します。

// 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);
};

実装クラスは両方のクラスを継承します。class MYGAME_API AMyChest : public AActor, public IMyInteractable のように記述し、virtual void OnInteract_Implementation(AActor* InstigatorActor) override; をオーバーライドします。呼び出し側から OnInteract を直接呼び出すことはありません。

// 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)
}

コンポジションパターン

深い継承階層ではなく、フラットなアクターとコンポーネントの組み合わせ(AMyCharacter + UMyHealthComponent + UMyInventoryComponent)を優先します。また、バリエーションごとにサブクラスを作るのではなく、1 つのクラスと複数のデータアセットを使います。コンポーネント間の通信にはオーナー経由(GetOwner()->FindComponentByClass<UMyHealthComponent>())またはデリゲートを使用し、兄弟コンポーネントへの生ポインターをキャッシュしないでください。データ駆動のロードアウトでは、データアセット上の TArray<TSubclassOf<UActorComponent>> から実行時にコンポーネントを追加します。

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
    }
}

プラグインや Game Features によってアクターへ注入されるコンポーネントには、ModularGameplay の UGameFrameworkComponentManager::AddReceiver(AActor* Receiver, bool bAddOnlyInGameWorlds = true) を使用します(5.8 ではベータ)。ue-game-features を参照してください。

非推奨 — 使用しないでください

使用しない5.8 で使用ソース
FindComponentByInterface<UMyInterface>()FindComponentByInterface<IMyInterface>()GameFramework/Actor.h:3844 の UE_DEPRECATED(5.5)
PreDuplicate(FObjectDuplicationParameters&)PreDuplicateFromRoot(FObjectDuplicationParameters& DupParams)GameFramework/Actor.h:2413 の UE_DEPRECATED(5.6)
Actor->LastRenderTimeGetLastRenderTime() / SetLastRenderTime(float)GameFramework/Actor.h:138 の UE_DEPRECATED(5.6)
NetUpdateFrequency = 30.f、MinNetUpdateFrequency = ...、NetCullDistanceSquared = ...SetNetUpdateFrequency()、SetMinNetUpdateFrequency()、SetNetCullDistanceSquared()GameFramework/Actor.h:898-909 の UE_DEPRECATED(5.5)
BeginReplication() / EndReplication() のオーバーライドOnReplicationStartedForIris / OnStopReplicationForIris / FillReplicationParams。ue-networking-replication を参照GameFramework/Actor.h:3493-3506 の UE_DEPRECATED(5.7)
APawn::GetMovementBase()GetMovementBaseObject() / GetMovementBaseInterfaceData()。ue-character-movement を参照GameFramework/Pawn.h:59 の UE_DEPRECATED(5.8)
AttachTo(...)、K2_AttachTo(...)、AttachRootComponentTo(...)AttachToComponent(Parent, FAttachmentTransformRules, SocketName)GameFramework/Actor.h:1991、Components/SceneComponent.h:740 の UE_DEPRECATED(4.17)
AttachRootComponentToActor(...)AttachToActor(ParentActor, FAttachmentTransformRules, SocketName)GameFramework/Actor.h:2018 の UE_DEPRECATED(4.17)
DetachFromParent(bool)、DetachRootComponentFromParent(bool)DetachFromComponent(FDetachmentTransformRules)、DetachFromActor(FDetachmentTransformRules)Components/SceneComponent.h:768 の UE_DEPRECATED(4.12)、GameFramework/Actor.h:2045 の UE_DEPRECATED(4.17)
Comp->bReplicates = trueSetIsReplicatedByDefault(true)(コンストラクター内)または SetIsReplicated(true)Components/ActorComponent.h:264-267 のプライベートメンバー
Comp->bCreatedByConstructionScriptComp->CreationMethod == EComponentCreationMethod::UserConstructionScript または IsCreatedByConstructionScript()Components/ActorComponent.h:306-310 の @deprecated Replaced by CreationMethod
SceneComp->RelativeLocation = ...、SceneComp->bVisible = ...、SceneComp->AttachParentSetRelativeLocation()、SetVisibility()、GetAttachParent()Components/SceneComponent.h:109-183 のプライベートメンバー
Actor->bHiddenIsHidden() / SetActorHiddenInGame(bool)GameFramework/Actor.h:365 のプライベートメンバー
C++ での GetComponentsByClass(...)(C++ 名は K2_GetComponentsByClass)GetComponents<T>(OutArray) または TInlineComponentArray<T*>GameFramework/Actor.h:3801-3807 の「ブループリントでのみ使用することを想定」
プリミティブの LightmapTypeGetLightmapType() / SetLightmapType()Components/PrimitiveComponent.h:359 の UE_DEPRECATED(5.5)

よくある間違い

コンストラクターでのワールドへのアクセス: CDO では GetWorld() は nullptr です。SpawnActor、タイマー、検索処理は BeginPlay で、コンポーネントのバインドは PostInitializeComponents で行います。

Super:: の呼び出しを省略する: ライフサイクルのオーバーライドでは、すべて Super:: を呼び出します(BeginPlay では最初に、EndPlay(EndPlayReason) では最後に呼び出します)。AActor::EndPlay は、コンポーネントに EndPlay を転送します。RouteEndPlay は Super::EndPlay の呼び出し漏れに対する安全策です(Actor.cpp:3230)。

UPROPERTY の外にあるプール済みまたはキャッシュ済みのアクター: 通常の TArray<AMyProjectile*> メンバーは GC から認識されません。UPROPERTY() TArray<TObjectPtr<AMyProjectile>> として宣言してください。UObject 以外のコードで非所有参照を使う場合は、TWeakObjectPtr<AActor> を使い、IsValid() を確認してください。

コンストラクターでの AttachToComponent: ヘッダー上は未登録コンポーネントにも使えますが、ここで使うことを想定した呼び出しは SetupAttachment です(SceneComponent.h:745-746)。BeginPlay 以降で AttachToComponent を使い、その bool の戻り値を確認してください。

RegisterComponent を呼び出さずに NewObject を使う: コンポーネントに描画状態や物理状態が設定されず、ティックも実行されません。また、Details パネルに表示し、シリアライズ後も保持する必要がある場合は、AddInstanceComponent も呼び出してください。

Deferred Spawn 後に FinishSpawning を呼び出さない: アクターが不正な状態のままになります(Actor.h:632)。必ず SpawnActorDeferred と FinishSpawning を組み合わせてください。

インターフェースメソッドを直接呼び出す: Target->OnInteract(...) では Blueprint の実装が呼び出されません。Implements<UMyInteractable>() で確認し、IMyInteractable::Execute_OnInteract(Target, ...) を通じて呼び出してください。

Tick でポーリングする: 毎フレーム if (HealthComp->IsDead()) を実行する代わりに、OnDeath を一度バインドしてください。毎フレームの処理が不要になったら、SetActorTickEnabled(false) でティックを無効にしてください。

PostInitializeComponents で子アクターが存在すると想定する: UChildActorComponent の子アクターは、プレイ開始が遅れることがあります(Actor.h:275)。BeginPlay 中またはその後に GetChildActor() を読み取ってください。

コンポーネントのプライベートフラグを書き換える: bReplicates、bIsActive、bHasBegunPlay、RelativeLocation、bVisible はプライベートです。上記の表に記載されているセッターとクエリーを使ってください。

関連スキル

  • ue-cpp-foundations — UCLASS/UPROPERTY/UFUNCTION の指定子、TObjectPtr と TWeakObjectPtr の違い、サブシステム、GC のルール
  • ue-gameplay-framework — GameMode、GameState、PlayerController、Pawn、Character、GAS を使わないダメージ処理
  • ue-physics-collision — コリジョンチャンネルとプロファイル、オーバーラップイベントとヒットイベント、スイープ、非同期物理ティック
  • ue-networking-replication — SetReplicates、プロパティのレプリケーション、OnRep、RPC、サブオブジェクトのレプリケーション、Iris
  • ue-gameplay-cameras — USpringArmComponent、UCameraComponent、PlayerCameraManager、Gameplay Cameras プラグイン
  • ue-blueprint-cpp-interop — Blueprint で実装可能なインターフェース、BlueprintNativeEvent のルール、潜在アクション、メタ指定子
  • ue-character-movement — CharacterMovementComponent、移動ベース API、CMC と Mover の比較
  • ue-animation-system — USkeletalMeshComponent のアニメーション、AnimInstance、モンタージュ
  • ue-audio-system — UAudioComponent の再生、減衰、MetaSounds
  • ue-niagara-effects — UNiagaraComponent、システムのスポーン、ユーザーパラメーター
  • ue-game-features — ModularGameplay コンポーネント、UGameFrameworkComponentManager、Game Feature アクション
  • ue-gameplay-abilities — アクター上の UAbilitySystemComponent の配置
  • ue-state-trees — アクターと AI コントローラー上の UStateTreeComponent
  • ue-ai-navigation — 知覚コンポーネントとナビゲーションコンポーネント
  • ue-procedural-generation — UProceduralMeshComponent、UDynamicMeshComponent、大規模なインスタンシング
  • ue-editor-tools — 詳細パネルのカスタマイズ、エディターユーティリティウィジェット、UToolMenus とエディターサブシステム
  • ue-input-system — Enhanced Input のアクション、マッピングコンテキスト、トリガー、モディファイアー、ユーザー設定
  • ue-mass-entity — Mass のプロセッサー、フラグメント、クエリー、エンティティ特性
  • ue-materials-rendering — マテリアルインスタンス、パラメーターコレクション、レンダーターゲット、ポストプロセス
  • ue-sequencer-cinematics — Level Sequence、再生、シネカメラ、Movie Render Graph
  • ue-world-level-streaming — World Partition、レベルストリーミング、データレイヤー、トラベル

コメント