[DevLog #003] ダンジョンから帰るボタンを作ったら、FCSの「再利用ウィジェット」の罠に落ちた話

DevLog

はじめに

今回はダンジョンのゴール地点、いわゆる「帰還ポータル」を実装しました。

「ポータルに触れたらメニューが出て、街へ帰るか次の階層へ進むか選べる」。

これもやはり、文字で書くと簡単です。でも FCS(Flexible Combat System)のアセットを使いながら実装していくと、独自のハマりポイントがいくつもありました。

今回はその記録です。


何を作ったか

ダンジョンのゴール地点(BP_DungeonGoal)に近づいてインタラクトすると、メニュー UI が開きます。

ポータルに近づく
→ Eキーを押す(インタラクト)
→ ポータルメニュー(WBP_PortalMenu)が開く
→「次の階層へ」「街へ帰還する」「閉じる」の3択
→ 選択に応じてサーバー側が処理

また、ポータルから離れたときに開きっぱなしのメニューが強制的に閉じる「フェイルセーフ処理」も入れています。


詰まったこと1:FCSのボタンには直接 OnClicked が出てこなかった

WBP_PortalMenu を作り、中に FCS が提供する再利用ウィジェット WB_Button を3つ配置しました。

ところが、通常の Button を置いたときのように「Details パネルに OnClicked が表示される」ことを期待していたら、何も出てきませんでした。

原因:WB_Button はラッパー構造だった

WB_Button は FCS が用意した UserWidget で、内部に本物の Button を持つ「ラッパー(包み紙)」です。

つまり、親ウィジェット(WBP_PortalMenu)から見ると、配置したのは Button ではなく UserWidget なので、直接 OnClicked にアクセスできないのです。

解決策:Event Dispatcher を追加して親に通知する

FCS の設計思想に則り、WB_Button に OnButtonClicked という Event Dispatcher を追加しました。

内部 Button の On Clicked
→ Play Sound 2D(FCS標準の効果音)
→ Call OnButtonClicked(Dispatcher を発火)

WBP_PortalMenu 側では、配置した各 WB_Button を選択したときに Details に現れる On Button Clicked イベントを受け取って、用途ごとの処理を書けるようにしました。

この構成にすると、FCS の見た目・効果音・フォーカス制御はそのまま使いつつ、クリック通知だけを外側へ伝えられます。今後 FCS の WB_Button を使うときは、毎回同じパターンで対応できます。


詰まったこと2:作業中に別のバグが突然表面化した

WB_Button 周りの作業中、突然 WBP_Input(ゲームパッド入力ウィジェット)のコンパイルが通らなくなりました。

Bind Event to OnButtonPressed が赤エラー
OnButtonPressed_Event が赤エラー
invalid delegate...

原因:BP_PlayerCharacter に Dispatcher が存在していなかった

WBP_Input は、BP_PlayerCharacter に OnButtonPressed という Event Dispatcher があることを前提として、Bind Event を組んでいました。

ところが、実際の BP_PlayerCharacter にはその Dispatcher が存在していませんでした。

つまり、「受け取り側はある前提で書かれていた」「送り側には定義が無かった」という不整合です。おそらく以前の実装時に定義が漏れていたものが、今回 WB_Button 周りの依存関係を整理したタイミングで表面化したのだと思います。

対処:まず Dispatcher を追加し、Bind Event を作り直す

手順は以下の2ステップです。

  1. BP_PlayerCharacter に OnButtonPressed を Event Dispatcher として追加(引数:ButtonName: NameIsPressed: Boolean
  2. WBP_Input の壊れたバインドを削除して、新しく作り直す

ここで重要なのは「作り直す」という点です。既存の赤いバインドノードは修復できません。一度削除して再構築するのが確実な対処法です。

Event Dispatcher に関わる実装では、

  • Dispatcher の名前を変えた
  • 引数の型を変えた
  • Dispatcher 自体を削除した

このいずれかが起きると、参照側の Bind Event が壊れます。「何もしていないのにコンパイルエラーが増えた」という現象の多くは、この連鎖が原因です。壊れたら作り直す前提でいると、あまり慌てずに済みます。


設計のポイント:RunPhase リセットで排他制御を実現

帰還処理の終わりに、BP_MyGameState が管理する RunPhase 変数を Town(街フェーズ)にリセットする処理を追加しました。

これにより、帰還ワープの最中に別のプレイヤーが同じボタンを押しても「フェーズが Town でないので無視」という挙動になります。

複雑なフラグ管理をしなくても、フェーズ変数1つで自然な排他制御が成立します。マルチプレイでのゲーム状態管理は、基本的にこの考え方が効きます。


確認できたこと:LootComponent のアイテムは帰還後も保持される

以前から「帰還したときにインベントリが消えないか」が懸念事項でした。

実際に帰還フローを通してテストしたところ、FCS LootComponent のアイテムは帰還後も正常に保持されていることを確認できました。

ダンジョンで拾ったものは持ち帰れる、という基本的な体験が成立したことになります。


今日学んだこと

  • FCS の WB_Button は OnClicked が直接出ない。Event Dispatcher を追加して親に通知する構成にする。
  • Event Dispatcher の Bind Event は壊れたら作り直す。既存のノードは修復できない。
  • RunPhase リセットをフロー終端に置くだけで、マルチプレイの排他制御が自然に成立する。

現在の状況

ダンジョン探索の「入場 → フロア進行 → 帰還」のループが全て動くようになりました。

次はいよいよコア戦闘の実装に入ります。FCS を使って「15〜25秒で1体倒せる」戦闘感触を作るのが目標です。


次回:FCS を使ったコア戦闘の実装と、気持ちよさをどう作るか

タイトルとURLをコピーしました