12
5

More than 1 year has passed since last update.

【ScriptableObject】シングルトンを使わずにマルチシーンのイベント管理してみる

Last updated at Posted at 2021-12-21

はじめに

本記事は、サムザップ Advent Calendar 2021 の12/21の記事です。
昨日の記事は@kanasaki_kenziさんのリマインドツールで少しでもストレスを減らそうでした。

こちらの記事は以前チーム制作をしていた際にマルチシーンを活用したいと思ったが、シーン間のイベントやデータの受け渡しに困ったのでそちらを解決した紹介になります。

問題点

マルチシーンを活用しようと思うとイベントのアタッチや受け渡しができないという問題にあたりました。
DefalutEvent.gif

シングルトンやStaticな変数を使うのも神クラスができそうでちょっとやだな~と思い、そこで、今回の主役であるScriptable Objectを使用することを考えました。

ScriptableObjectとは

  1. スクリプトインスタンスから独立している
  2. 大量の共有データを保存できる
  3. データが一つなので、メモリの節約につながる
  4. エディター上から数値を変更できる

image.png

Eventとは

ここで明記しているEventとはUnityEngine.Eventsのことである登録した関数を指定のタイミングで
呼んでくれる機能です。プレイヤー死亡時にアニメーションやエフェクト・敵の挙動
ステートマシーンへのシーン遷移など複数のクラスに影響を与える時などすごく便利です!

ScriptableObjectを使ったイベント

今回の本題。ScriptableObjectを使用したイベント管理システムです。
このシステムは2つのクラスがいります。

  1. ScriptableObjectを継承したGameEventクラス
  2. MonoBehaviourを継承したGameEventListenerクラス

 [コード例] GameEvent ScriptableObject

[CreateAssetMenu]
public class GameEvent : ScriptableObject
{
    private List<GameEventListener> listeners = new List<GameEventListener>();

    public void Raise()
    {
        for(int i = listeners.Count -1; i >= 0; i--)
            listeners[i].OnEventRaised();
    }

    public void RegisterListener(GameEventListener listener)
    {  
        listeners.Add(listener);
    }

    public void UnregisterListener(GameEventListener listener)
    {
        listeners.Remove(listener);
    }
}

 [コード例] GameEventListener

using UnityEngine;
using UnityEngine.Events;

public class GameEventListener : MonoBehaviour
{
    public GameEvent Event;

    public UnityEvent Response;

    private void OnEnable()
    {
        Event.RegisterListener(this);
    }

    private void OnDisable()
    {
        Event.UnregisterListener(this);
    }

    public void OnEventRaised()
    {
        Response.Invoke();
    }
}

 イベントの流れ

  1. GameEvent から Raise() が呼ばれる
  2. GameEventListenerのリストOnEventRaised() 呼ばれる
  3. イベントの発行

イベント作成
MakeEvent.gif

イベントリスナーへのアタッチ
AddGameEventlistener.gif

イベントの設定
SetEvent.gif

イベントの呼び出し

public class PlayerController : MonoBehaviour
{
    [SerializeField, Header("プレイヤーステータス")]
    private StatusParam playerStatus = null;
    [SerializeField, Header("プレイヤー死亡イベント")]
    private GameEvent gameEvent = null;

    void Start()
    {
        // 値の変更イベント
        playerStatus.hp.mChanged += (data) =>
        {
            if (data <= 0)
            {
                // イベント呼び出し
                gameEvent.Raise();
            }
        };
    }
}

今回の問題点である同じシーンでしかイベントを配置できない問題ですが、ScriptableObjectが管理することによって スクリプトインスタンスから独立している特徴を活かしマルチシーンでのイベント管理を有効にできました。

最後に

今回のScriptableObjectの使用方法は正直作っていて驚きました。
なぜなら、ScriptableObject=データとして 扱うといった固定概念があったため、Scriptable Objectをまるでシングルトンパターンの マネージャーのような扱い方ができることが衝撃的でした。
また、個人的に好きだった点としてイベントの作成が 視覚的でわかりやすい点です。
イベント管理というと便利だが、作っているときに複雑でわからなくなることもありますが、この方法は イベントを作成→購読者にアタッチ→関数の登録といった誰にでも理解できるところがいい点だと感じました。
最後まで読んでいただきありがとうございました。

明日は@kida_hironariさんの記事です。

12
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
5