SmartObjects と StateTree で AI の実装を試してみた

# 概要
SmartObjects と StateTree を試してみた記事です。
今回は簡単にキューブの周りをジャンプし続ける AI をつくってみます。
# 環境
Unreal Engine 5.3.2
Windows11

## 有効にするプラグインたち

これらのプラグインを有効にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/00d599b0-9566-e7c8-7b08-fa8a6ec14f7a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/97d949ff-4fd6-b4ff-1ac0-d32fa27c5b53.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/7ebb6810-df2f-0add-5ceb-8d53064241c2.png)

# SmartObject の作成

まずただのアクターを作成します。名前は B_TestSmartObject と名付けました。このアクターに StaticMesh コンポーネントと SmartObject コンポーネントを追加しこのようにアタッチします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/f02fd3e2-cb2e-2d56-4137-243e79e49020.png)

StaticMesh コンポーネントには Cube を割り当てました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/64635515-e1e4-b6b9-8223-b96e4c09f5f1.png)

次に SmartObject の定義アセットを作ります。SmartObjectDefinition を選択し作成します。名前は SO_TestSODefinition と名付けました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/ae77dc8a-f13f-b61e-0bd7-3de1ace783e8.png)

B_TestSmartObject の SmartObject コンポーネントを選択し、先ほど作成した、SO_TestSODefinition を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/a443b0b5-7533-f87f-4f03-ace4343bfff9.png)

次に SmartObject にインタラクトした際の行動やインタラクト位置などを実装していきます。
GameplayBehavior ブループリントを作成します。B_SO_TestBehavior と名付けました。SmartObject にインタラクトした時の挙動をここに書きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/e490a5b9-65d9-852e-a86a-42a95ff586eb.png)

こんな感じの実装です。SmartObject をトリガーしたキャラクターをジャンプさせるだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/f7de7dd0-f56f-1fb3-5f92-f2e8f2cf069a.png)

次に B_SO_TestBehavior を SO_TestSODefinition に設定します。
SO_TestSODefinition アセットを開き Details欄にて下記のように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/c1d0270b-653d-6e9d-506e-958340c81830.png)

同じくDetails欄で Slots を2個追加します。この Slot のロケーションにAIがインタラクトを行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/357f91e1-de50-a2d2-93b2-41edd36fd738.png)

追加した Slot を開くと色々設定できるので、色と位置を変更してこのようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/79c06f0f-3ed6-1137-d20b-a68655854159.png)

最後に B_TestSmartObject を開き SmartObject コンポーネントを選択すると Slot の位置を調整できるので下記のように調整しました。ここで Cube と Slot の位置が大きく重なってしまうと AIアクター が SmartObject に到達できなくなってしまうので注意です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/70fba2c4-a925-0c29-7809-e94a703ee2ae.png)

ここまでで SmartObject 側で作成したアセットのまとめです。以下のような関係です。
- B_TestSmartObject
    - SmartObjectコンポーネントを持つ SmartObject となるアクター
- SO_TestSODefinition
    - SmartObject の定義を行うデータアセット
- B_SO_TestBehavior
    - GameplayBehavior型のBPアセット。SO_TestSODefinition に設定する

これで SmartObject 側の実装が完了しました。次は AIアクター が SmartObject の検索や SmartObject を "Use" する実装を StateTree を用いて作っていきます。

# AIアクターが SmartObjects を検索して使用する実装

 AIアクターとなる Character ブループリントを作成します。B_TestSO_AI と名付けました。
 StateTree コンポーネントを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/dd1bfcc4-e957-9a0d-f286-0c6615a7bd91.png)

StateTreeアセットを作成します。ST_TestSO と名付けました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/81fc1116-b0c6-6da4-dea5-39cc4162ed30.png)

これを先ほど作成した AIアクターである B_TestSO_AI の StateTree コンポーネントに設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/e588bb8a-9c3c-d105-f451-102fbc32d4bf.png)

次に StateTree のタスクを実装します。StateTreeTaskBlueprintBase を親にブループリントを作成します。STT_FindAndUseSO と名付けました。このステートツリータスク内で SmartObject を検索し使用する実装を書きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/c83ed3ab-abe8-6ca0-0264-63105be8924d.png)

STT_FindAndUseSO を開き SmartObject を検索し使用する実装をしていきます。ここの実装は公式ドキュメントに BehaviorTree 版の実装があるので主に StateTree に関する箇所をメインに軽く解説します。

https://docs.unrealengine.com/5.3/ja/smart-objects-in-unreal-engine---quick-start/

STT_FindAndUseSO アセットで Context Actor という Actor型の変数を作成し、Instance Editable にチェックをします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/4805acec-e90c-2d31-fd8f-d5abaf61389d.png)

EnterState イベントノードを引っ張り以下のような実装を書きます。EnterState はあるステートに入ったら実行されるイベントノードです。
ここでは4000 * 4000 のボックス内の GameplayBehavior を実装した SmartObject を検索しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/2c996bdf-2f7a-cd73-82fd-1fa3f42c7833.png)

次に SmartObject が持つ使用可能な Slot の要求を行います。
検索した結果から SmartObjectHandle を取り出し `Mark Smart Object Slot as Claimed` ノードによって SmartObject が持つ使用可能な Slot の要求を行います。これにより、Slot の状態が Claimed (要求中) になります。
ちなみに5.3のドキュメントでは Claim というノードになっていますがこれは Deprecated になっており、`Mark Smart Object Slot as Claimed` ノードを使うのが正しいようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/2940ba04-4be4-b9d0-c9b0-fac114f517b7.png)

SmartObject の検索と要求をした後には SmartObject を使用します。
`Move to and Use Smart Object with Gameplay Behavior` ノードを利用します。これは AIアクターが SmartObject の場所まで移動し SmartObject の起動も行ってくれる便利ノードです。
最後に FinishTask を接続して StateTree タスクの実装は完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/9ab3633d-8fc0-f88c-f227-5b5d5c66c435.png)

次は StateTree 側の設定を行います。ST_TestSO を開いて、Context Actor Class に Actor を設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/8489240c-fb07-4e3b-e090-3c3edac53da9.png)

次に先ほど実装した StateTreeタスクである STT_FindAndUseSO を StateTree で使用できるように設定します。 
Add State で State を一つ追加します。State の名前は TestSO にしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/2ce708ce-4880-776a-0afc-b5b689d0b191.png)

追加した State を選択し Details欄にて、Tasks を追加し、STT_FindAndUseSO を選択します。これを開くと タスク内で作成した変数である Context Actor 変数が設定できるので、StateTree の Actor をバインドします。
ここでいう Context Actor は StateTree の適用者である AIアクターの B_TestSO_AI にあたります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/7b92a6a2-f611-ca04-33fe-4bee976812d6.png)

ここまでで AIアクター側の実装が完了しました。

AIアクター側で作成したアセットのまとめです。以下のような関係です。
- B_TestSO_AI
    - StateTree コンポーネントを持つ Character クラスである AIアクター
- ST_TestSO
    - AIアクターの StateTree。 B_TestSO_AI の StateTree コンポーネントに設定 
- STT_FindAndUseSO
    - SO の検索・使用を行う StateTreeTask

# 実行して確認

SmartObject の実装とそれを利用する AIアクターの実装ができたので、実際にレベルに配置して実行してみます。AIとしては大したことはしていないですが、SmartObject と StateTree を利用して AI の行動を定義できました。

![ezgif.com-video-to-gif-converted.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953062/725df595-4723-aa29-be3d-6a97d54ab9c1.gif)



# 感想
SmartObjects と StateTree について今回紹介した機能は一部に過ぎません。特に StateTree はあまり触れられていませんが興味を持っていただけたら幸いです。
ここまで読んでいただきありがとうございました。

# 参考リンク
https://docs.unrealengine.com/5.3/ja/smart-objects-in-unreal-engine---overview/

https://docs.unrealengine.com/5.3/ja/overview-of-state-tree-in-unreal-engine/

https://www.docswell.com/s/EpicGamesJapan/KGPLMK-UE_CEDEC2022_MassFramework?ref=rss#p36