ぼっちプログラマのメモ

Unreal Engineについて書いたりしてます

【UE5】StateTreeでドアの開閉状態の管理を実装してみる (BPのみ編)

はじめに

StateTreeはいいぞ!

StateTreeはUE5から追加された「汎用的な階層型ステートマシン」機能です。UEユーザの中で「ステートマシン」と言えばAnimationBPのAnimGraphにおけるステートマシンがまず思い浮かぶかと思います。基本的な概念・考え方はStateTreeでも同様で、「State(状態)を条件・処理に応じてTransition(遷移)していくことでロジックを実装できる」というものです。

ただStateTreeはAnimGraphと比べると遥かに高機能かつ柔軟な機能なので、まさに汎用的に様々なロジック実装に使用することができます!

  • NPCの振る舞い制御
  • 敵キャラの戦闘行動選択
  • オブジェクトの状態管理
  • アニメーション選択
  • エストロジック
  • などなど

そして最近は海外のUnreal Festで講演されることが増えてきたり、UE5.4, 5.5で大きく改善されたりと盛り上がりつつある機能です。

ということで、まずは「ドアの開閉制御・管理」というシンプルな処理をStateTree かつ BPのみで実装してみました!

まあこれだけだと「BPのみでええやん」となりますが、開閉時にSEなどの演出をつけたり、特定のカギを持ってないと開かない、などの制御追加をStateTree側でできるのが便利なはず…!

なお、StateTreeの基本的な操作・機能に関しては既に素晴らしい記事が沢山あるので割愛します(本当にありがとうございます!めちゃ勉強になりました!)

Unreal Engine の StateTree | Unreal Engine 5.5 ドキュメンテーション | Epic Developer Community

[UE5] StateTreeで状態管理|株式会社ヒストリア

StateTreeの構成について #ue5 - Qiita

State Tree(5.5) まとめ #ue5 - Qiita

State Treeを使ってクエストの進行管理をしてみた | ドクセル

Your First 60 Minutes with StateTree | Tutorial

StateTree | UE5攻略リンク

検証環境はUE5.5.1 です。また、ドアのBPは機能別サンプル(ContentExample)のBP_Doorを用いています。なお、今回はBPオンリーで実装していきますが、C++を使って更にいい感じに実装する方法を後日公開予定です。

「StateTreeでドアの開閉状態の管理を実装」の全体像

いきなり操作説明から入ると混乱するので、まずは全体像をざっくり箇条書き

それぞれの実装・担当範囲

  • ドアBP
    • 開く・閉じるアニメーション(Open, Close)
    • ドアの開閉フラグ
    • キャラが指定範囲を出入りしたことをStateTreeに通知
      • 本当はBP_Door外に処理を移したい所ですが、本筋ではないので割愛
  • StateTree
    • 3つの状態(Idle, Open, Close)
      • Open(Close)状態ではドアBPのOpen(Close)アニメーションを再生
    • ドアBPから通知されたらOpen, Close に遷移するための中間State(Interact)
      1. 現在の「ドアの開閉フラグ」に応じて、Open、Close状態に遷移
      2. X秒後、待機状態に戻る

BP_Doorの実装変更

① BP_Doorの開閉処理をBegin( End ) Overlapではなく、専用イベントから呼び出すように

② State Treeコンポーネントを追加(State Tree AI ではないので注意)

③ Begin, End Overlap に State Tree への通知処理( Send State Tree Event ) を追加

  • GameplayTagも専用のものを新規追加

ドアのOpen/Close用のStateTree Taskを作成

StateTreeTaskBlueprintBase で StateTree Task用 BPを作成

② ドアBPのOpen/Close イベントを StateTree Task の Enter State イベントで呼び出すように実装

  • Actorプロパティのカテゴリを Contextに することを推奨。しておくと、StateTreeで使用した際に自動的にドアBPと紐づきます
  • Taskが完了したことを通知するFinish Taskノードは呼びま…せん!これは後述の通り、Delay Taskノードと併用するためです。
    • Finish Taskノードが呼ばれるとそのState自身も終了し、同時に実行した別Taskが完了まで進まないため。Finish Taskノードを呼ぶか否かをプロパティで公開し、StateTree側で設定する形式がオススメ
    • 参考:https://jeanpaulsoftware.com/2024/08/13/state-tree-hell/

StateTreeアセットのセットアップ

StateTree Component 設定でStateTreeアセットを作成し、Context Actor Class を ドアBPに設定
- 各State、TaskにてドアBPが持つプロパティにアクセスするため ② 各Stateを追加・配置
- Theme/Colors 機能を用いて、各Stateをカテゴリ毎に色分けしておくと分かりやすい

③ Root Stateに「ドアBPからの通知を受けたら、Interact Stateに遷移する」Transitions処理を追加

④ Wait State にデバッグ表示(Debug TextTask)を追加
- Reference Actorは Context の Actor を手動で再設定しないと、座標(0,0,0)にテキストが表示されるので注意

⑤ Interact State の Enter Conditions を設定し、専用の通知処理以外で遷移しないようにする

⑥ Open State に、遷移条件とデバッグ表示を追加
- Condition「Bool Compare」を用いて、Actor ( ドアBP)の IsClosed フラグが 有効 ( = 閉じている)場合は遷移可能に

⑦ Opening State に、「0.5秒待つ処理」と「ドアBPのOpen処理を呼び出す自作Task」を追加

⑧ 同様の操作・設定を Close, Closing State に行う
- Condition, 呼び出す自作Taskは逆になるので注意

ドアBPに作成したStateTreeアセットを登録

追加したStateTreeコンポーネントに、先程作成したStateTree アセットを設定

これで完成です!実行すると、以下の流れで処理が走るようになります!

① 操作キャラクタがドアBPが持つVolumeに触れる・離れた際に、InteractイベントをStateTreeに送信
② StateTreeがドアの状態に応じて、ドアBPのOpen/Closeイベントを呼び出し

さいごに

シンプルな内容ではありましたが、これまでBP側でゴリゴリ実装していた処理を StateTreeを使うことで外部に逃がすことができました!

それによりデータドリブンによる制御が可能になるため、「ドアの開閉時の演出」や「特定のアイテムを持っていないと開かないドアなどの特殊処理」をBP/C++を弄らなくても追加・変更しやすくなります!(ここまでくるとデータドリブンというよりはスクリプト的な形にはなるなぁとは思いつつ)

ただ今回の作り方だと…ドアBP自身が持つプロパティ・イベントに強く依存しているため、例えば「他のギミック制御でもStateTreeを使いたい!」という場合に似たような処理を新規実装する必要があります。それではStateTreeの旨味が少なくなってしまうので、次の記事では 少しC++を使うことでより汎用的なStateTreeを作ってみようと思います。

おしまい