概要
UnrealEngine4のアクタのTick処理順序についてのメモ。
TickGroup単位より細かい制御についての調査メモとなります。
参考
以下の記事を参考にいたしました、ありがとうございます。
UE4公式:アクタのティック
Tick関数がどう処理されているかUnrealC++を追ってみよう!
UE4でActor間のTick実行順序に依存関係を持たせる方法
UE4でTick実行依存関係の設定とポーズ中のTick実行を併用すると問題が起こる
環境
Windows10
Visual Studio 2017
UnrealEngine 4.26
Tick処理について
一定間隔(通常毎フレーム)で処理を回す仕組みです。処理自体の有無、処理間隔などを細かく設定できます。
ActorTick と ComponentTick は独立しているようなので、Tick不要なアクターのTickを切ってもコンポーネントは動いている場合があるので要注意です。
void AMyActor::BeginPlay()
{
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bStartWithTickEnabled = true;
}
物理演算の前後などをエディタ上で設定できます。
大抵のTick処理順序はこれで済ますかと思われます。
Tick Interval
は 0.0 なら毎フレーム処理が回ります。
Tickグループの定義は以下のように定義されています。(Engine\Source\Runtime\Engine\Classes\Engine\EngineBaseType.h)
TG_StartPhysics
TG_EndPhysics
TG_LastDemotable
などはエディタで設定できないようにメタ情報が設定されています。
/ **ティック関数が属するティックグループを決定します。 * /
UENUM(BlueprintType)
列挙型ETickingGroup
{{
/ **物理シミュレーションを開始する前に実行する必要のあるアイテム。 * /
TG_PrePhysics UMETA(DisplayName = "Pre Physics"),
/ **物理シミュレーションを開始する特別なティックグループ。 * /
TG_StartPhysics UMETA(Hidden、DisplayName = "Start Physics"),
/ **物理シミュレーション作業と並行して実行できるアイテム。 * /
TG_DuringPhysics UMETA(DisplayName = "During Physics"),
/ **物理シミュレーションを終了する特別なティックグループ。 * /
TG_EndPhysics UMETA(Hidden、DisplayName = "End Physics"),
/ **実行する前に剛体と布のシミュレーションを完了する必要があるアイテム。 * /
TG_PostPhysics UMETA(DisplayName = "Post Physics"),
/ **チェックされる前に更新作業を行う必要があるアイテム。 * /
TG_PostUpdateWork UMETA(DisplayName = "Post Update Work"),
/ **最後まで降格されたものすべてをキャッチします。 * /
TG_LastDemotable UMETA(Hidden、DisplayName = "Last Demotable"),
/ **実際にはティックグループではない特別なティックグループ。すべてのティックグループの後、実行する新しく生成されたアイテムがなくなるまで、これが繰り返し再実行されます。 * /
TG_NewlySpawned UMETA(Hidden、DisplayName = "Newly Spawned"),
TG_MAX,
};
TickGroupを設定するには以下のメソッドを使います。
UFUNCTION(BlueprintCallable, Category="Utilities", meta=(Keywords = "dependency"))
void SetTickGroup(ETickingGroup NewTickGroup);
Tickの依存関係を設定する
Tick処理はアクターとコンポーネントは各自独立しているため(アクターのTickを切ってもそのアクターが保持しているコンポーネントのTickは処理される)
各アクター、コンポーネントごとに先に処理をされる前提を設定することができるようです。
Actorクラス
Engine\Source\Runtime\Engine\Classes\GameFramework\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
/** 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依存関係を設定する例です。
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class AActor* PrerequisiteActor;
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
にて AddTickPrerequisiteComponent
で MyActorComponent
に対して依存関係を設定した場合の出力例です。
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実行順序を制御する機能があるのはわかったのですが、多数のアクターやコンポーネントを制御するにはあまり向いていないような感じです。
(限定的な状況でつかうべき?)
これならTickを使わずにマネージャのようなクラスを作成してそこから毎フレーム更新用のメソッドを定義して呼び出したほうが管理しやすいと思います。作成の手間がかかりますが、Tickの有無や優先を考えるにはそのほうがいいような。。
良い使い方がありましたらどなたか教えてください。