Edited at

[UE4] CharacterのTickの依存関係を制御する際の注意点

時間のない人やTickの順序に関して知っている人はこれだけ守っていただければよいかと思います。

ルール: SkeletalMeshのTickGroupは、Tickの依存関係設定などによってSkeletalMesh自身が持つTickGroupから変更されないようにする

Tickの依存関係を独自設定してこのルールを破ってしまわないよう注意してください。SkeletalMeshのTickGroupが初期設定と異なる場合、そのMeshのアニメーション処理などは全てメインスレッドでの処理となります。例えあなたが32コアのCPUを持っていても、たった1コアでアニメーションのUpdateもEvaluationも処理されることになるのでお気をつけください。

※以前、上記のルールを「SkeletalMeshのTickGroupはTG_PrePhysicsでないとならない」と記載しておりました。これはSkeletalMeshのTickGroupがデフォルトではTG_PrePhysicsだからなのですが、独自のキャラクターを作成した場合このデフォルト値を変更できるため、TG_PrePhysicsに限定するのは誤っているとのご指摘を頂き確かにそれは正しいため、上記文言を変更いたしました。大変失礼いたしました。

以下では、上の注意点を説明するために、Tickの順序に関してのおさらいと、上記ルールの理由をご説明します。


Tickに関してのおさらい


Tickとは

UE4はレベル、Actor、Componentが毎フレーム呼び出す処理を記述することができ、これをTick()と読んでいます。

image.png


Tick Groupとは

各Tickがゲームの処理の中のどのタイミングで呼び出されるかを指定するために、Tick Groupという設定があります。

各種Tick Groupの一覧

enum 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,
};

アクターのTickのTick Groupの設定箇所

image.png

各種Tick Groupの中で、どのような順序で各オブジェクトのTickが呼び出せれるかというと、そちらは基本的にはランダムと考えて良いのですが、その詳細をCom04さんが下記スライドに説明してくださっておりますのでそちらご参照ください。(QiitaはSlideShare埋めこめねえのかよおおおおおお!!!!!)


Tickの依存関係を設定する

ただ、”AのTickはBのTickよりも必ず前に走って欲しい。。”といった需要は確かにあり、それを満たすために、UE4では依存関係を設定する"Add Tick prerequisites Actor/Component"という関数が用意されています。

これらの使い方などはほげたつさんの以下のブログに詳しく記載がされていますので、こちらを参考にしてみてください。

ほげたつブログ:UE4でActor間のTick実行順序に依存関係を持たせる方法


Tickの依存関係を確認する

この依存関係がどのように設定されているか?UE4では dumpticksコマンドで1フレーム内の呼び出されたTickを表示することができます。そうすると、以下の様に階層的に依存関係が表示されます。今回の場合は、キャラクターのMovementComponetよりも前にThirdPersonCahracterのアクターのTickが走る様に設定されているのがわかります。(みやすさのために途中を端折っています)

ThirdPersonCharacter_C, ...ThirdPersonCharacter_167[TickActor], ... Prerequesities: 1

CharacterMovementComponent, .... ThirdPersonCharacter_167.CharMoveComp...

ここまでが、TickとTickの順序に関する簡単なおさらいです。この後が本題となります。キャラクターアニメーションが絡んだTick制御の際、気をつけないとCPU負荷が激増することがあるのでその注意点をご紹介したいと思います。


ルール: SkeletalMeshのTickGroupは、Tickの依存関係設定などによってSkeletalMesh自身が持つTickGroupから変更されないようにする

依存関係制御により他のTickGroupでSkeletalMeshを走らせてはいけません。何故かと言うと、SkeletalMeshのアニメーションが並列に走るためには様々なルールがあるのですが、その中を抜粋すると以下の様な記述があります。

const bool bDoParallelUpdate = ... && (TickFunction->GetActualTickGroup() == TickFunction->TickGroup) && ...;

const bool bDoParallelEvaluation = ... && (TickFunction->GetActualTickGroup() == TickFunction->TickGroup) &&...;

この式の意味することは 「”依存関係などを考慮して設定された、実際にこのTickが計算されるTickGroup(ActualTickGroup)”と"このSkeletalMesh自身が持つTickGroup"が一緒じゃないと並列計算できないよ」 というものです。

このルールを外れると、アニメーションの並列計算がなくなり、たった数体のキャラクターしかいなくてもGame Threadの負荷が激増します。以前調査したタイトルでは、Tickの依存関係をかなり複雑につくっており、知らないうちにこのルールをやぶっており、実機で動かしてみたら200msぐらいのCPU負荷になっていたなんてことがありました。実機で動かしたら異常なことにすぐ気づくかとは思うのですが、原因を調査するのがすこしやっかいかもしれないので、頭の片隅に入れておいて頂けると幸いです。

以上、簡単ですが、まとめさせていただきました。