[UE4] VR上で動くキャラクターを捕まえる

# はじめに

VR上で小さくて動き回るキャラクターを捕まえるアプリを作成したのですが、いくつか躓いた箇所があったので記事を書いておきます。UE4.25.1を使用しています。動作確認はOulusQuestで実施していますが、特にOulusQuestに依存する処理はないので他のヘッドセットでも多分問題ないです。完成イメージは以下動画のような感じです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ニワトリをつかむのやっとできた！<br>ビヘイビアーツリーを中断したり再開する処理が難しかったのと、MovementMode変更しないとダメなのになかなか気が付かなかった <a href="https://t.co/iOKAVJizr2">pic.twitter.com/iOKAVJizr2</a></p>&mdash; 高浜 (@SatoshiTakahama) <a href="https://twitter.com/SatoshiTakahama/status/1282264363399647232?ref_src=twsrc%5Etfw">July 12, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 作り方
## 準備
・プロジェクトはVRテンプレートを使用します。一部流用したいコンテンツがあるため、コンテンツブラウザ上で新規追加=>機能またはコンテンツパックを追加からサードパーソンを追加します。
・キャラクターとして使用するアセットをマーケットプレイスで入手してプロジェクトに追加しておきます。自分は今回FARM ANIMALS PACKというアセットからニワトリを使用していますが、なんでも構わないと思います。

## キャラクターの作成
・コンテンツブラウザ上で、新規追加=>ブループリントクラスを選択すると、下記のようなダイアログが表示されるので、Characterを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/6a952766-3a9e-ffd9-4390-4c231372ab54.png)
・生成されたBPの名前をAI_ChickenCharacterに変更します。
・メッシュを自分が使用したいキャラクターのものに差し替えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/fa28bbc4-cd37-518c-77f1-6ccecdf5c269.png)
・CapsuleComponentの大きさをキャラクターに合わせて調整しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/cc20052c-2926-1092-d295-661bc9654867.png)

## アニメーションBPの作成
・コンテンツブラウザ上で、新規追加=>アニメーション=>アニメーションBPを選択すると、下記のようなダイアログが表示されるので、自分が使用したいキャラクターのスケルトンを選択してOKを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/fc8e48f7-35df-e25f-f5bd-d36a8464d021.png)
・生成されたアニメーションBPの名前をChicken_AnimBPに変更し、編集画面を起動します。
・Float型のSpeedと、Boolean型のIsInAir?の２個の変数を追加します。
・Mannequinフォルダ=>AnimationsフォルダにあるThirdPerson_AnimBPの編集画面を開いて、イベントグラフ上の処理をすべて選択してコピーし、Chicken_AnimBPのイベントグラフにペーストします。
・Chicken_AnimBPのAnimGraphの画面を開き、ステートマシンのノードを追加して出力ポーズにつなぎます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/6ffa9852-d0cd-09c7-f223-886863a2e13c.png)
・ステートマシンをダブルクリックして編集画面を開きます。画面のアセットブラウザ上に選択できるアニメーションの一覧が表示されるので、そこからアニメーションをドラッグしてステートマシンの画面上に持ってくることでノードを作成することができます。待機時のアニメーションとしてIdleのノードを作成、歩行時のアニメーションとしてWalkのノードを作成してノード間をつなぎます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/3dbe8b4b-1672-e2bc-10d7-374c710992ba.png)
・ノード間の遷移条件は以下のように設定してスピードを反映して切り替わるようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/29924773-e6f2-2554-14e6-bfd0c793aa34.png)

## キャラクターにアニメーションBPを反映
・再びAI_ChickenCharacterの編集画面を開いて、AnimClassに先ほど作成したアニメーションBPのChicken_AnimBPを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/8fe33167-5351-53e1-bba5-5452c30533b7.png)

## ビヘイビアツリーの作成
今回、一定時間毎に移動可能な位置をランダムに選択して移動する動作のビヘイビアツリーを作成していきます。
・コンテンツブラウザ上で、新規追加=>AI=>ビヘイビアツリーを選択してビヘイビアツリーを生成し、名前をBT_ChickenAIに変更します。
・BT_ChickenAIの編集画面を起動し、新規ブラックボードをクリックすると、同じフォルダにブラックボードが生成されるので、名前をBB_ChickenAIに変更します。
・BT_ChickenAIの編集画面で、ブラックボードをBB_ChickenAIに変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/e3bc5cec-b013-4543-eb6a-7d6097ebe61d.png)
・BT_ChickenAIの編集画面右上のブラックボードをクリックして編集モードを切り替えます。新規キーを押して、Vector型のTargetLocationと、Bool型のCatchedの2個を作成します。
・編集モードをビヘイビアツリーに戻して、新規タスクをクリックすると、同じフォルダにタスクが生成されるので、名前をBTT_FindNavigableLocationに変更します。
・BTT_FindNavigableLocationの編集画面を開いて、変数TargetLocationKeyを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/d82a0017-5231-f30e-42f8-b6737154b098.png)
・BTT_FindNavigableLocationに以下のようにノードを追加します。キャラクターを中心に移動可能な範囲で位置を取得し、それをブラックボードのキーに設定する流れになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/59752e5f-0481-df30-e559-46f2c4d4ac16.png)
・BT_ChickenAIの編集画面を開いて、ノードを追加していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/272df199-0412-a3ef-fde1-6b015e356208.png)
① Selectorを追加します。一見意味がなさそうですが、これがないと②のノードのデコレーターが機能しなかったので追加しました。
② Sequenceを追加します。その後Sequenceを選択した状態で右クリック=>デコレーターを追加でBlackBoardを選択します。以下のように設定し、キーCatchedの値が更新されたときにビヘイビアツリーを中断するようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/e0ad37a2-4e89-62e3-9417-0c02a4a5e9bf.png)
③ タスクのBTT Find Navigable Locationを追加し、キーTargetLocationを設定します。
④ タスクのMoveToを追加し、キーTargetLocationを設定します。
⑤ タスクのWaitを追加します。

## AIコントローラーの作成
・コンテンツブラウザ上で、新規追加=>ブループリントクラスを選択すると、下記のようなダイアログが表示されるので、AIControllerを選択してOKを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/83ee87e4-2ee6-06fb-4be3-b955975571f0.png)
・生成されたBPの名前をAIC_ChikenCharacterに変更します。
・AIC_ChikenCharacterの編集画面を起動し、イベントOnProcessと、RunBehaviorTreeのノードを追加します。RunBehaviorTreeには先ほど作成したビヘイビアツリーBT_ChickenAIを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/df5217be-e622-0d24-f5fa-4fff0f7dec6b.png)
・今度はAI_ChickenCharacterの編集画面を起動し、ポーンの設定を変更して、作成したAIコントローラーAIC_ChikenCharacterが使われるようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/da6519c2-f0c8-6e92-13b5-a475b56cc811.png)

## キャラクターを配置
AI_ChickenCharacterをシーン上の適当な位置に配置します。ここまで作成した状態で実行してみると、キャラクターが一定時間ごとにランダムな位置に動き、移動するときにアニメーションが切り替わるようになっているはずです。

## キャラクターをつかむBPの追加
・コンテンツブラウザ上で、新規追加=>ブループリント=>ブループリントインターフェースを選択してブループリントインターフェースを生成し、名前をIBP_Catchedに変更します。
・IBP_Catchedの編集画面を開いて、関数名をCatchedに変更し、インプットとアウトプットを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/2df34785-fc3c-4507-a666-f26e7b87013b.png)
・今度はAI_ChickenCharacterの編集画面を起動し、クラス設定を変更して、下記２個のインターフェースを追加します。PickupActorInterfaceは、VRテンプレートに含まれている、つかんだり放したりするためのインターフェースです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/0fddb116-6e9a-cbd5-5b36-cc3d30a79e1e.png)
・そのままAI_ChickenCharacterでマイブループリントのChatchedをダブルクリックすると、処理のひな型が表示されるので、以下のようにノードを作成します。これでChatchedが実行されると、ブラックボードにあるキーのChatchedの値が更新されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/6e193d9f-0e88-3ea3-8d09-08b91aee229b.png)
・さらにAI_ChickenCharacterのイベントグラフの画面で以下のようにノードを作成します。イベントPickupはキャラクターが掴まれたときに実行開始され、Chatchedでキャラクターのビヘイビアーツリーを停止、SetMovementModeを変更して持ち上げられるようにして、AttatchComponentでキャラクターを手のコンポーネントにくっつけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/28823b7c-4d6b-e900-0ddc-fb0c8698360e.png)
・さらにAI_ChickenCharacterのイベントグラフの画面で以下のようにノードを作成します。イベントDropはキャラクターが放されたときに実行開始され、DetachFromActorでキャラクターを手のコンポーネントから外し、SetActorRotationでキャラクターを地面に対して水平向きに調整して、SetMovementModeを変更してキャラクターが動けるようにして、Chatchedでキャラクターのビヘイビアーツリーを再開します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/29aa2e21-50e9-bc72-6448-c776a531e0ed.png)

## 完成
ここまで作成した状態で実行すると、キャラクターをつかんだり放したりできるようになっているはずです。

# 参考資料
・[ブループリントを使用した AI の紹介](https://learn.unrealengine.com/course/3441156)
公式のラーニングのコースですが、一通り動画を見ながら作業することでビヘイビアツリーの基礎的な使い方を理解できます。動画は英語ですが日本語の字幕が付いています。１点だけ自分がはまったポイントがあるのですが、ゲームプレイデバッガーを起動するときにデフォルトの設定ではできないので、同時押しを伴わない設定に変更する必要があります。

# 追記
これまでは手を放したときにその場ですぐにキャラクターが動きを再開していたのですが、少し手を加えることでキャラクターを放り投げられるようにできました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">SetSimulatePhysicsを設定してニワトリを投げられるようになった <a href="https://t.co/jM9cI4l904">pic.twitter.com/jM9cI4l904</a></p>&mdash; 高浜 (@SatoshiTakahama) <a href="https://twitter.com/SatoshiTakahama/status/1285539681883484160?ref_src=twsrc%5Etfw">July 21, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
## キャラクターをつかむBPの改造
・AI_ChickenCharacterの編集画面を起動し、Bool型の変数Droppedを追加します。
・さらにAI_ChickenCharacterのイベントグラフの画面で、イベントDropのノードを変更します。キャラクターが放されたときにSetSimulatePhysicsを設定してキャラクターに物理的な挙動が反映されるようにしていったん処理を終了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/f482bb2d-f5b3-4968-9adc-1584753a93cc.png)
・AI_ChickenCharacterのイベントグラフの画面で以下のようにノードを追加します。キャラクターが地面に落ちたらタイマーを発動し、一定時間後に元の動きに戻すような流れになります。
(2020/09/26追記)
以下ではSetTimerノードのLoopingにチェックが入っていますが、チェックを入れないでください。チェックを入れた状態では複数回イベントが発生し、以降の挙動がおかしくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/67035cdd-4a0f-c6dd-427b-48909bc655bd.png)
・AI_ChickenCharacterのコリジョンの設定を変更します。これで改造は完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215278/28688d7c-f87e-6d25-f69a-b57995eb3585.png)