オブジェクトプール機能

目次

  概要
  MonobitEngine.IMunPrefabPoolを継承したオブジェクトプール専用クラスの生成
  MonobitEngine.MonobitNetwork.ObjectPool プロパティ


概要

あらかじめシーン内に「これから同期させるオブジェクト」を読み込んでおく技法

  この項目でも技術的な話を取り上げます。

  同期オブジェクトを扱う際、オブジェクト同期のための送受信データの通信負荷もさることながら
  オブジェクトのインスタンス生成段階における「ローディング処理」に多大な負荷が掛かります。
例えばUnity はマルチスレッドで動作させることは可能ですが、
UnityEngine.Object.Instantiate() などの UnityAPI の呼び出し自体はメインスレッドしかできないという特性がありますので、
UnityEngine.Object.Instantiate() が終了するまでの間「実行結果を待つ」必要があります。
  そのため、Unityにおいて大量多数のオブジェクト登場させる場合、都度スパイクが発生する問題が出てしまいます。

  これを防ぐために Unity では オブジェクトプール(ObjectPooling) と呼ばれる技法がよく使われます。
  簡単に説明すれば、シーン進行中においてこれから登場するオブジェクトをシーン内に非表示で読み込ませておき、
  登場/消滅時に必要な分だけ表示/非表示に設定する方法です。
  これによりシーン開始時にスパイクは発生しますが、シーン進行中においてはシームレスに進行させることができます。

MUNでオブジェクトプールを実行するためには

  オブジェクトプールは UnityEngine.Object.Instatiate() を使用する範囲であれば この方法 で問題なく実装できますが、
  MonobitEngine.MonobitNetwork.Instatiate() や MoobitEngine.MonobitNetwork.InstantiateSceneObject() などを利用した同期オブジェクトの場合、
・オブジェクトプールとして生成したクライアントがオブジェクト所有権を有するという仕様があるため、
 「自身のオブジェクト」のプールはできても、「接続先相手」のプールはできない。

・途中入室したクライアントに対し、既存側・新規側を問わず必要以上の読み込み待ちのスパイクが発生する。

これにより、頻繁にルームへの入退室が可能なシステムの場合、かえってパフォーマンスが悪化してしまう。
  という問題点が発生してしまいます。

  これを回避するために、MUNでオブジェクトプールを実装する場合には、ある手順に則って作成しなければなりません。


MonobitEngine.IMunPrefabPoolを継承したオブジェクトプール専用クラスの生成

MUNにおけるオブジェクトプール実装部分

  MUNにおけるオブジェクトプールの実装のためには条件があり、
  そのうちの1つが以下に説明する MonobitEngine.IMunPrefabPoolを継承したクラスに実装することです。

  MonobitEngine.IMunPrefabPool には Instantiate() および Destroy() の抽象メソッドが存在し、
  これ以降に触れる MonobitEngine.MonobitNetwork.ObjectPool プロパティ にクラスインスタンスを適用することで
    ・ MonobitEngine.MonobitNetwork.Instatiate() 系のメソッドによる同期オブジェクトのインスタンス生成
    ・ MonobitEngine.MonobitNetwork.Destroy() 系のメソッドによる同期オブジェクトのインスタンス破棄
  について外部から操作することができます。

  以下は、その点を意識した上での IMunPrefabPool 継承クラスの実装例です。
    ※ origin, maxCount については別途 UnityEditor の inspector から値を設定する必要があります。
using UnityEngine;
using MonobitEngine;
using System.Collections.Generic;
namespace TestMunFeature
{
    public class PrefabPool : MonobitEngine.IMunPrefabPool
    {
        [SerializeField]
        private static GameObject origin; // Instatiate 生成元となるゲームオブジェクト(プレハブ)

       [SerializeField]
       private static int maxCount ; // プールするオブジェクト数

        private static List<GameObject> poolList = new List<GameObject>(); // オブジェクトプールとして登録されたインスタンスの実体

        // コンストラクタ
        public PrefabPool()
        {
            // maxCount 分だけ、非アクティブかつシーン遷移で破棄されないインスタンスを生成する
            for (int i = 0; i < maxCount; i++)
            {
                obj = GameObject.Instantiate(origin, Vector3.zero, Quaternion.identity) as GameObject;
                if (obj != null)
                {
                    GameObject.DontDestroyOnLoad(obj); // シーン遷移で破棄させない
                    obj.SetActive(false); // 非アクティブ設定
                    poolList.Add(obj); // リスト追加
                }
           }
        }
        // オブジェクト動的生成のカスタマイズ
        public GameObject Instantiate(string prefabName, Vector3 position, Quaternion rotation)
        {
            // インスタンスを非アクティブからアクティブに変更
            GameObject go = null;
            foreach (GameObject obj in poolList)
            {
                if (!obj.activeSelf)
                {
                    go = obj;
                    go.SetActive(true);
                    break;
                }
            }
            // アクティブに変更したインスタンスの transform 情報を設定
            if (go != null)
            {
                go.transform.position = position;
                go.transform.rotation = rotation;
            }
            return go;
        }
 
       // オブジェクト動的破棄のカスタマイズ
        public void Destroy(GameObject obj)
        {
            MonobitView view = obj.GetComponent<MonobitView>();
            if (view != null && view.isMine)
            {
                // 自身のオブジェクトであれば非アクティブ化
                obj.SetActive(false);
            }
            else
            {
                // 他クライアントからのオブジェクトであれば破棄
                GameObject.Destroy(obj);
            }
        }

        // 明示的なデストラクタ
        public static void DestroyAll()
        {
            foreach (GameObject obj in poolList)
            {
                MonobitNetwork.Destroy(obj);
                GameObject.Destroy(obj);
            }
            poolList.Clear();
        }
    }
}



MonobitEngine.MonobitNetwork.ObjectPool プロパティ

機能

  MonobitEngine.IMunPrefabPool を継承したクラスインスタンスを登録し、オブジェクトプール機能を有効にします。

  登録されるクラスは MonobitEngine.IMunPrefabPoolを継承したオブジェクトプール専用クラスの生成 の例に従い、
  クラスメソッドに Instantiate() および Destroy() が内包されている必要があります。
  また、この値のデフォルト値は null で、この場合、オブジェクトプール機能は有効にはなりません。

入出力型

内容
MonobitEngine.IMunPrefabPool オブジェクトプールを実装しているクラスインスタンス。
デフォルト値は null です。

記述例

// ObjectPool に IMunPrefabPool を継承したクラスインスタンスを登録します。
MonobitEngine.MonobitNetwork.ObjectPool = new PrefabPool();