UnrealEngine　アニメーション設計②　UE5のLyraStarterGameのAnimationシステムを見てみよう

# はじめに
１回目ではC++のクラス設計についてお話しました。
そこで、処理負荷が高くなってしまうためAnimBPのイベントグラフは使わずC++で書くと言いましたが、
EpicのサンプルであるLyraStarterGameではどうしているか見てみようと思います。

# Tour#1　ABP_Mannequin_Baseのイベントグラフ
これがLyraStarterGameのベースとなるAnimBPです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/b08d1d50-ad46-b481-a1a0-ceaf80288bd5.png)

イベントグラフを見てみましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/9a787551-6577-d27b-bafb-ef8444964f8b.png)

要約すると
```
イベントグラフはパフォーマンスのボトルネックになるためUE5で新しく追加された
BlueprintThreadsafeUpdateAnimation関数に処理を書きました。これは並列実行されます。
```

Lyraでもイベントグラフは空でした。でも、C++化するのではなくBPを並列実行できるようにして対策したようです。
BlueprintThreadsafeUpdateAnimation関数を見てみましょう


# Tour#2 BlueprintThreadsafeUpdateAnimation関数
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/4bbddd50-9a53-3793-1cf5-f1d471bdeafe.png)

要約すると
```
この関数はアニメーションを選択するために必要なゲームデータを収集しています。
しかし、スレッドセーフ関数はゲームオブチェクとを直接参照することはできません。
代わりにProperty Access systemを使ってデータにアクセスします。
```

この関数は並列実行されるため、ワーカースレッド上で実行されます。１回目でお話したマルチスレッドの注意点がここでも言及されていますね。

PropertyAccessSystemが自動的にパラメータをコピーしてくれるみたいです。

# PropertyAccessSystemとは？
右クリックから"PropertyAccess"と検索すると出てきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/43e6f321-8a97-43fe-78bb-5c8947e91fad.png)

作るとこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/232eb37c-db53-c624-38f8-fc00f9634bb3.png)

「バインド」をクリックして関数又はプロパティを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/079e5596-ead4-450d-6ace-436ae979a34a.png)

ここでバインドした値をGameThreadで事前にコピーしておいてくれるという仕組みのようです。
独自の関数をリストに追加するにはUAnimInstanceを継承したクラスにUFUNCTIONでBPに公開するするだけでよい。
```
	UFUNCTION(BlueprintPure)
	ALyraCharacter* GetLyraCharacter() const { return nullptr; }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/79650963-0f36-f65c-6e7c-08a65f80de3c.png)

コピーのタイミングも選択できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/7a279c73-3911-dad3-7fcf-076bf28b4f3b.png)

自動とスレッドセーフって何でしょう？ドキュメントが無くてわからん！
エンジンの中身を調べるしかなさそうなので調べてみましょう。

Enumの定義はこれみたいです
```cpp
UENUM()
enum class EAnimPropertyAccessCallSite
{
	// Access is made on a worker thread in the anim graph or in a BP function
	WorkerThread_Unbatched UMETA(DisplayName="Thread Safe"),

	// Access is made on a worker thread before BlueprintThreadSafeUpdateAnimation is run
	WorkerThread_Batched_PreEventGraph UMETA(DisplayName="Pre-Thread Safe Update Animation"),

	// Access is made on a worker thread after BlueprintThreadSafeUpdateAnimation is run
	WorkerThread_Batched_PostEventGraph UMETA(DisplayName="Post-Thread Safe Update Animation"),
	
	// Access is made on the game thread before the event graph (and NativeUpdateAnimation) is run
	GameThread_Batched_PreEventGraph UMETA(DisplayName="Pre-Event Graph"),

	// Access is made on the game thread after the event graph (and NativeUpdateAnimation) is run
	GameThread_Batched_PostEventGraph UMETA(DisplayName="Post-Event Graph"),
};
```
「スレッドセーフ」は何もしない直接参照みたいですね。

「自動」はコピー元と先が両方スレッドセーフの場合は何もしない「スレッドセーフ」、
そうじゃない場合はゲームスレッドで事前にコピーされる「ゲームスレッド（プリイベントグラフ）」
になるっぽいですね。
```cpp
		if(InContext.ContextId == ContextId_Automatic)
		{
			if(InContext.bSourceThreadSafe && InContext.bDestThreadSafe)
			{
				// Can only be in the worker thread batch if both endpoints are thread-safe
				return (int32)EAnimPropertyAccessCallSite::WorkerThread_Unbatched;
			}
			else
			{
				return (int32)EAnimPropertyAccessCallSite::GameThread_Batched_PreEventGraph;
			}
		}
```

# IKとかどうやってるの？
IKするためには足の下の地面の高さを取得する必要があります。ワーカースレッドのBPからレイキャストってできなかった気がするけど、どうやってるのか調べてみました。

ABP_Mannequin_BaseのコントロールリグでCR_Mannequin_FootPlantが登録されていて
そこでTraceされてました。ControlRigの処理負荷ってどんなもんなんでしょうね。足のIKくらいだったら自前で処理書いたほうが早い気がするのですが今度UE5のIKRigとか調べてみたいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/266bd709-004c-c92e-d12e-1513da277249.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/aaf44e6a-4560-ba55-f3ee-e0a5a52cdf80.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778561/912525ed-afc9-192a-52d1-caf7309cfe71.png)

# UE4やC++でも並列処理にすることはできる
FAnimInstanceProxy::Updateをオーバーライドしてそこに処理を書けばc++でも並列処理にすることはできます。
ただ、C++の処理がそこまで重たくないのであれば並列処理する必要はないと思います。
実際に自分がやっているプロジェクトではほとんど変わらず、あまり意味がなかったので元に戻しました。

# まとめ
- Blueprintが好きな方はLyraのやり方が良いでしょう。
BPの処理はワーカースレッドで行われるため処理負荷を気にする必要なさそうです。

- C++が好きな方はアニメーション設計①で説明したやり方が良いでしょう。
C++で書けば処理負荷は十分軽いのでゲームスレッドで処理しても問題ないでしょう。
そのほうがスレッドセーフじゃない関数も使えて便利です。

---
[UnrealEngine　アニメーション設計①　スレッドセーフなクラス設計](https://qiita.com/okqi/items/4291d872921ff6abe025)