Unity: FindObjectは使わないようにする

Unite2013にて「Internal Unity Tips and Tricks」という発表での内容。Unity内でいろんな場所からあるオブジェクトにアクセスするためにはFindObjectを使うのだといろんな技術書にも書かれているけども、凄腕開発者に言わせるとFindObjectは参照に伴う負荷が高いらしいので、マネージャークラスを作成して、シングルトン配列にオブジェクトを格納しておき、それを参照する方が良いとのこと。

これが通常のFindObject実装。これが負荷が高いとのこと。


#define SLOW_LOOKUP

using UnityEngine;

public class SomeGameLogic : MonoBehaviour
{
    // Update is called once per frame
#if SLOW_LOOKUP
    void Update ()
    {
        if (Input.GetKeyDown(KeyCode.Space))
            var towers = FindObjectsOfType(typeof (Tower)) as Tower[];
            for (int i = 0; i < towers.Length; i++)
            {
                towers[i].DoAction();
            }
        }
    }
}

上記実装を使わず、以下のようなマネージャークラスを作成する。


using System.Collections.Generic;
using UnityEngine;

public class TowerManager : MonoBehaviour
{
    public static TowerManager Singleton;
    public List _Towers = new List();

    public void OnEnable()
    {
        if (Singleton == null)
            Singleton = this;
    }

    public void AddTower(Tower t)
    {
        if (!_Towers.Contains(t))
        {
            _Towers.Add(t);
        }
    }

    public void RemoveTower(Tower t)
    {
        _Towers.Remove(t);
    }
}

このマネージャークラスはシングルトンとして存在し、オブジェクト(この発表ではTower)を格納するためのリスト配列を持っている。リストに追加・削除するためのパブリックメソッドも定義されている。

そして、各タワー側には、自分をリストに追加するために、以下のような実装を持たせる


using UnityEngine;

public class Tower : MonoBehaviour {
    void OnEnable()
    {
        TowerManager.Singleton.AddTower(this);
    }

    void OnDisable()
    {
        TowerManager.Singleton.RemoveTower(this);
    }

    public void DoAction()
    {

    }
}

そうすることで、冒頭のFindObjectの実装部分を以下のように書き換えることができる。


#define FAST_LOOKUP

using UnityEngine;

public class SomeGameLogic : MonoBehaviour
{
    // Update is called once per frame
#if FAST_LOOKUP
    void Update ()
    {
        if (Input.GetKeyDown(KeyCode.Space))
            for (int i = 0; i < TowerManager.Singleton._Towers.Count; i++)
            {
                TowerManager.Singleton._Towers[i].DoAction();
            }
        }
    }
}

あとは、TowerManagerはTowerよりも先に読み込んでおく必要があるので、Unityメニューの Edit > Project Settings > Script Execution Order のパネル上でTowerManagerの読み込み順が先になるように設定しておくこと。

なるほど参考になりました。