LoginSignup
10
11

More than 1 year has passed since last update.

UE4 Tickの処理順序についてのメモ

Posted at

概要

UnrealEngine4のアクタのTick処理順序についてのメモ。
TickGroup単位より細かい制御についての調査メモとなります。

参考

以下の記事を参考にいたしました、ありがとうございます。
UE4公式:アクタのティック
Tick関数がどう処理されているかUnrealC++を追ってみよう!
UE4でActor間のTick実行順序に依存関係を持たせる方法
UE4でTick実行依存関係の設定とポーズ中のTick実行を併用すると問題が起こる

環境

Windows10
Visual Studio 2017
UnrealEngine 4.26

Tick処理について

一定間隔(通常毎フレーム)で処理を回す仕組みです。処理自体の有無、処理間隔などを細かく設定できます。
ActorTick と ComponentTick は独立しているようなので、Tick不要なアクターのTickを切ってもコンポーネントは動いている場合があるので要注意です。

MyTestActor.cpp
void AMyActor::BeginPlay()
{
    PrimaryActorTick.bCanEverTick = true;
    PrimaryActorTick.bStartWithTickEnabled = true;
}

物理演算の前後などをエディタ上で設定できます。
大抵のTick処理順序はこれで済ますかと思われます。
Tick Intervalは 0.0 なら毎フレーム処理が回ります。

TickGroup.png

Tickグループの定義は以下のように定義されています。(Engine\Source\Runtime\Engine\Classes\Engine\EngineBaseType.h)
TG_StartPhysics TG_EndPhysics TG_LastDemotable などはエディタで設定できないようにメタ情報が設定されています。

EngineBaseType.h
/ **ティック関数が属するティックグループを決定します。 * /
UENUMBlueprintType
列挙型ETickingGroup
{{
/ **物理シミュレーションを開始する前に実行する必要のあるアイテム。 * /
TG_PrePhysics UMETADisplayName = "Pre Physics",

/ **物理シミュレーションを開始する特別なティックグループ。 * /
TG_StartPhysics UMETAHiddenDisplayName = "Start Physics",

/ **物理シミュレーション作業と並行して実行できるアイテム。 * /
TG_DuringPhysics UMETADisplayName = "During Physics",

/ **物理シミュレーションを終了する特別なティックグループ。 * /
TG_EndPhysics UMETAHiddenDisplayName = "End Physics",

/ **実行する前に剛体と布のシミュレーションを完了する必要があるアイテム。 * /
TG_PostPhysics UMETADisplayName = "Post Physics",

/ **チェックされる前に更新作業を行う必要があるアイテム。 * /
TG_PostUpdateWork UMETADisplayName = "Post Update Work",

/ **最後まで降格されたものすべてをキャッチします。 * /
TG_LastDemotable UMETAHiddenDisplayName = "Last Demotable",

/ **実際にはティックグループではない特別なティックグループ。すべてのティックグループの後、実行する新しく生成されたアイテムがなくなるまで、これが繰り返し再実行されます。 * /
TG_NewlySpawned UMETAHiddenDisplayName = "Newly Spawned",

TG_MAX,
};

TickGroupを設定するには以下のメソッドを使います。

Actor.h
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
void SetTickGroup(ETickingGroup NewTickGroup);

Tickの依存関係を設定する

Tick処理はアクターとコンポーネントは各自独立しているため(アクターのTickを切ってもそのアクターが保持しているコンポーネントのTickは処理される)
各アクター、コンポーネントごとに先に処理をされる前提を設定することができるようです。

Actorクラス

Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h

Actor.h

/** PrerequisiteActorの後にこのアクターをチェックします。 これは、このアクターのティック機能にのみ適用されます。 所有するコンポーネントの依存関係は、必要に応じて個別に設定する必要があります。 */
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
virtual void AddTickPrerequisiteActor(AActor* PrerequisiteActor);

/** PrerequisiteComponentの後にこのアクターをチェックします。 これは、このアクターのティック機能にのみ適用されます。 所有するコンポーネントの依存関係は、必要に応じて個別に設定する必要があります。 */
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
virtual void AddTickPrerequisiteComponent(UActorComponent* PrerequisiteComponent);

/** PrerequisiteActorへのティックの依存関係を削除します。 */
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
virtual void RemoveTickPrerequisiteActor(AActor* PrerequisiteActor);

/** PrerequisiteComponentへのティックの依存関係を削除します。 */
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
virtual void RemoveTickPrerequisiteComponent(UActorComponent* PrerequisiteComponent);

Componentクラス

Engine\Source\Runtime\Engine\Classes\Components\ActorComponent.h

ActorComponent.h
/** PrerequisiteActorの後にこのコンポーネントをチェックします */
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
virtual void AddTickPrerequisiteActor(AActor* PrerequisiteActor);

/** PrerequisiteComponentの後にこのコンポーネントをチェックします。 */
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
virtual void AddTickPrerequisiteComponent(UActorComponent* PrerequisiteComponent);

/** PrerequisiteActorへのティックの依存関係を削除します。 */
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
virtual void RemoveTickPrerequisiteActor(AActor* PrerequisiteActor);

/** PrerequisiteComponentへのティックの依存関係を削除します。 */
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
virtual void RemoveTickPrerequisiteComponent(UActorComponent* PrerequisiteComponent);

設定例

優先させるアクターをメンバ変数として保持してセット/リセットのメソッドにてTick依存関係を設定する例です。

MyActor.h
UPROPERTY(EditAnywhere, BlueprintReadWrite)
    class AActor*   PrerequisiteActor;
MyActor.cpp
void AMyActor::BeginPlay()
{
    TSubclassOf<AMyActor> _FindClass = AMyActor::StaticClass();
    TArray<AActor*> _ActorList;
    UGameplayStatics::GetAllActorsOfClass(GetWorld(), _FindClass, _ActorList);

    int32 _Lp = 0;
    for (auto& _It : _ActorList) {
        if (_It == this) {
            if (_ActorList.Num() > _Lp + 1) {
                PrerequisiteActor = _ActorList[_Lp + 1];
            }
            break;
        }
        _Lp++;
    }
}

// 依存関係をセット
void AMyActor::Set()
{
    AddTickPrerequisiteActor(PrerequisiteActor);
}

// 依存関係をリセット
void AMyActor::Reset()
{
    RemoveTickPrerequisiteActor(PrerequisiteActor);
}

循環参照時のエラー出力

前提条件となるアクター(もしくはコンポーネント)の設定において循環するような設定をすると以下のようなエラーログが出力されますが、クラッシュしたりはしないようです。

アウトプットログ
LogTick: Warning: While processing prerequisites for MyTestActor_C /Game/FirstPersonCPP/Maps/UEDPIE_0_FirstPersonExampleMap.FirstPersonExampleMap:PersistentLevel.MyTestActor2_2[TickActor], could use MyTestActor_C /Game/FirstPersonCPP/Maps/UEDPIE_0_FirstPersonExampleMap.FirstPersonExampleMap:PersistentLevel.MyTestActor_8[TickActor] because it would form a cycle.

Tick依存関係の確認

dumpticks コマンドで依存関係の確認ができます。
以下は MyTestActor にて AddTickPrerequisiteComponentMyActorComponentに対して依存関係を設定した場合の出力例です。

設定前
MyTestActor_C /Game/FirstPersonCPP/Maps/UEDPIE_0_FirstPersonExampleMap.FirstPersonExampleMap:PersistentLevel.MyTestActor_8[TickActor], Enabled, ActualStartTickGroup: TG_PrePhysics, Prerequesities: 0
設定後
MyTestActor_C /Game/FirstPersonCPP/Maps/UEDPIE_0_FirstPersonExampleMap.FirstPersonExampleMap:PersistentLevel.MyTestActor_8[TickActor], Enabled, ActualStartTickGroup: TG_DuringPhysics, Prerequesities: 1
    MyActorComponent /Game/FirstPersonCPP/Maps/UEDPIE_0_FirstPersonExampleMap.FirstPersonExampleMap:PersistentLevel.MyTestActor_8.MyActorComponent3, MyActorComponent /Game/FirstPersonCPP/Maps/UEDPIE_0_FirstPersonExampleMap.FirstPersonExampleMap:PersistentLevel.MyTestActor_8.MyActorComponent3[TickComponent]

インデントされて前提条件となるアクターかコンポーネントのリストが表示されるようです。

まとめ

Tick実行順序を制御する機能があるのはわかったのですが、多数のアクターやコンポーネントを制御するにはあまり向いていないような感じです。
(限定的な状況でつかうべき?:thinking:
これならTickを使わずにマネージャのようなクラスを作成してそこから毎フレーム更新用のメソッドを定義して呼び出したほうが管理しやすいと思います。作成の手間がかかりますが、Tickの有無や優先を考えるにはそのほうがいいような。。
良い使い方がありましたらどなたか教えてください。

10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11