本ページにはプロモーションが含まれます
Playmaker

【Unity】インスタンスされたFSMから別のFMSの参照方法

この記事は約6分で読めます。

はじめに

Playmakerを使っていると、「インスタンス側のFSMから、元のGameManagerのFSMにイベントを送りたい」という場面がよくありますよね。スクリプトならthisや参照を持たせてメソッドを呼ぶだけですが、FSM同士だと「どうつなぐか」が分かりづらいところです。今回は、インスタンス化されたFSMからGameManager側のFSMを参照するための、シンプルなやり方をメモとして残しておきたいと思います。

やろうとしたこと

今回やりたかったことを、ざっくり言うと次の2点です。

  • 1. GameManager的なGameObject(以下GMO)にFSMを置いておき、そこからプレハブを複数インスタンス化する。
  • 2. インスタンス側のFSMから、呼び出し元であるGMOのFSMにイベントを送りたい。
Custom Scriptは、ボタンオブジェクトにいくつか変数を持たせるためのものです。

スクリプトで書く場合は、たとえば「Cloneしたインスタンスにアタッチしてあるコンポーネントから、呼び出し元スクリプトのメソッドをコールする」といった形になりますね。Playmakerでも同じことをしたいのですが、FSM同士を直接「呼び出し元参照」でつなぐような仕組みはありません。

結論としては、GameObject.Find系(タグなど)か、Playmakerのグローバル変数をうまく使って、「GMOを一度どこかに保持しておき、そこにSend Eventする」という形に落ち着きました。以下では、Playmakerだけで完結させる2パターンを紹介します。

方法1:Playmakerのグローバル変数を使う

1つ目は、Playmakerのグローバル変数でGMOを共有する方法です。

  • GMO自身のGameObjectを、Playmakerのグローバル変数に入れておきます。
  • インスタンス側のFSMでは、そのグローバル変数経由でGMOを参照します。
  • GMOのFSMに設定したグローバルトランジションへSend Eventします。

こうしておくと、「どのインスタンスからでも、同じGMOのFSMにイベントを投げられる」状態になります。毎回Find系アクションで探す必要がないので、FSMの配線もシンプルになりますね。

「正攻法かはともかく、運用的には一番わかりやすい」パターン。

メリット

  • 一度セットすれば、どのインスタンスFSMからも同じように参照できます。
  • 処理フローが「グローバル変数 → GMO FSMにSend Event」で統一されます。
  • 毎回Findしない分、FSMの見通しが良いです。

デメリット / 注意点

  • グローバル変数の数が増えてくると、管理がやや面倒になるかもしれません。
  • シーンまたぎで構成が複雑になると、「どこでセットしているか」を把握しておく必要があります。

方法2:タグとFind系アクションを使う

2つ目は、タグを使ってGMOを探す、Unityらしい方法です。概念としてはGameObject.FindGameObjectsWithTagと同じで、PlaymakerのFind Game Objectアクションを使ってGMOを取得し、ローカル変数に保持してからSend Eventします。

  • GMOに、たとえばtag_game_managerのようなタグを設定しておきます。
  • インスタンス側のFSMでFind Game Objectアクションを実行します。
  • 見つけたGMOをローカル変数に入れて、そこにSend Eventします。
tag_game_managerがGameManagerに設定したタグ。

メリット

  • Unityの通常のGameObject運用と感覚が近く、直感的です。
  • タグさえ決めてしまえば、シーン内のどこからでも同じように使えます。

デメリット / 注意点

  • 参照のタイミングによっては、非アクティブなオブジェクトや、まだロードされていないオブジェクトが取れないことがあります。
  • 毎回Find系アクションを挟むので、FSMのステートが増えがちです。
  • シーン間でFSMが行ったり来たりする構成だと、「どこで何を探しているか」を追うのが大変になるかもしれません。

どちらを使うか? 個人的なおすすめ

どちらの方法でも、インスタンス側のFSMから、GMO(GameManager)的なFSMを参照してイベントを送ることはできます。

実際に運用してみた感覚としては:

  • オブジェクト間の遷移先をシンプルにまとめたいときは「方法1(グローバル変数)」が一番わかりやすいです。
  • 既存のUnityプロジェクトでタグ運用がしっかり決まっているなら「方法2」でも十分だと思います。

特に、メニュー遷移やゲーム全体を管理するFSMなど、「常に1個だけ存在するGameManagerに向かってイベントを飛ばしたい」ケースでは、グローバル変数でGMOを握っておき、そこに向けてSend Eventを1回投げるだけ、という形が一番楽でした。

なお補足として、C#には「グローバル変数」は存在せず、静的メンバ(staticで近いことをやります。Playmakerのグローバル変数は、「FSM同士で共有するためのstatic的な入れ物」だとイメージしておくと理解しやすいと思います。

参考動画

Playmakerを使って、プレハブから他のオブジェクトを参照する手法としては、公式チャンネルのこちらの動画がとても参考になります。

おまけ:メニューボタンにIDを持たせて判定する

メニュー系のUIでは、「どのボタンが押されたか」をFSMだけで判定したくなる場面も多いですよね。そこで、ボタンとなるプレハブにカスタムスクリプトを追加して、インスタンス生成時にボタンの識別IDなどをプロパティに設定しておきます。

ボタンが押下されたときに、このIDを一緒に送ってやることで、受け取り側のFSMで「どのボタンから来たイベントか」を判定できるようになります。考え方としては、WEBフロントエンドでボタンにデータ属性を持たせるのと近いイメージですね。

以下のようなシンプルなスクリプトをボタンのプレハブにアタッチしておきます。

using UnityEngine;
/// <summary>
/// アタッチしたオブジェクトに変数を追加
/// </summary>
public class menuCustomValue : MonoBehaviour
{
    [SerializeField]
    public int sendValue;
    [SerializeField]
    public int sendRefValue;
    [SerializeField]
    public int sendOpenValue;
}

ボタン側のFSMでは、クリック時にこのmenuCustomValueから値を取得してイベントに乗せ、受け取り側のFSMでその値を見て遷移先を分岐すると、1種類のフローで複数ボタンをさばけるようになります。

メニューボタンに動的にプロパティ値を割り当てて識別しています。

FSMだけでUIを組んでいる場合でも、ちょっとした補助スクリプトを併用することで、「どのボタンが何を開くか」「どのIDを持っているか」といった情報をすっきり管理できるようになります。