読者です 読者をやめる 読者になる 読者になる

渋谷ほととぎす通信

完全趣味でやってるUnityメモ。説明できないところを説明できるようにするための個人ブログ。昨日の自分より少しでも大きく慣れるように。。。 ※所属団体とは一切関係がありません

Unity SetActiveとenabledどっちを使うべきかの考察


f:id:esakun:20150730215258g:plain

環境

  • Unity5.4.2f1

MonoBehaviourを継承したクラスにはAwakeやStartなど、Unityから特定のイベントを受け取れるメソッドを定義することができます。

以下は使用頻度が高い代表的なイベントですが、これらには実行順が決められています。

  1. Awake
  2. OnEnable
  3. Start
  4. Update

ただ実行される条件は、GameObject、そこのアタッチされたコンポーネントが共にアクティブ状態の場合です。

f:id:esakun:20161027215320p:plain
※アクティブ状態というのはこのようにチェックが入った状態

本記事では前半非アクティブからアクティブに切り替えた場合の実行順をまとめつつ、後半本題の SetActiveとenabledどっちを使うべきかについて言及していきます。

非アクティブの状態からアクティブに切り替えた場合の実行順

シーンロード時にGameObject、Component共に非アクティブの状態とします。

ちなみにこの状態では、どのイベントも実行されません。

パターン1. GameObject アクティブ | Component 非アクティブ
  1. Awake
パターン2. GameObject 非アクティブ / Component アクティブ

イベントが呼ばれません

パターン3. GameObject アクティブ / Component アクティブ
  1. Awake
  2. OnEnable
  3. Start
  4. Update
補足1. パターン1の後、Component アクティブ
  1. OnEnable
  2. Start
  3. Update

OnEnableから始まりStart, Updateが続きました。

補足2. 余談1後、GameObject or Componentを非アクティブにしてアクティブに戻す
  1. OnEnable
  2. Update

※GameObject, Componentともに同じ結果になります。

Awake、Startイベントはインスタンス生成後1度しか実行されないイベントです。
その後、アクティブの切り替えがトリガーで実行されるイベントはOnEnableです。 また、GameObject、Compoentをどちらか非アクティブにするとUpdateは停止します。

補足3. パターン3においてStartでコルーチン使った場合

要は以下のようなケースです。

IEnumerator Start () {
    Debug.Log("Start 1");
    // 1フレーム待機する
    yield return null;
    Debug.Log("Start 2");
}

void Update ()
{
    Debug.Log("Update");
}

ログはこのように出力されます。

Start 1
Update
Update
Start 2

Start関数が遅延実行の場合、Start関数処理実行中にUpdateは処理されます。
処理順番としてはStart -> Updateです。
しかしUpdateが 2回 呼ばれた後、1フレーム待機した Start 2 が実行されています。

理由はマニュアルに記載されています。

yield コルーチンは、次のフレームで Update 関数がすべて呼び出された後に続行します。

正解はこの一文です。

コルーチンはUpdateが終了してから実行されるため、Startメソッド内の コルーチン終了後のStart 2の処理Update処理の後に実行 されているため、2回連続Updateが呼ばれているように見えたというわけですね。ちょっとした補足でした。

ここまでのまとめ

  • GameObjectが非アクティブの場合はComponentがアクティブになっても何もイベントは走らない
  • Awakeは他と例外でComponentではなくGameObjectのアクティブがトリガーで実行される

少し視点を変えて

非アクティブの状態における関数実行の挙動も確認していきましょう。

1. GameObjectをアクティブ / Componentを非アクティブ

◇関数実行

可能

◇コルーチン実行

可能

2. GameObject 非アクティブ / Component アクティブ

◇関数実行

可能

◇コルーチン実行

不可(以下のエラーが出力されます)

Coroutine couldn’t be started because the the game object ‘〜〜〜’ is inactive!

3. GameObject アクティブ / Component アクティブ

◇関数実行

可能

◇コルーチン実行

可能

4. GameObject 非アクティブ / Component 非アクティブ

◇関数実行

可能

◇コルーチン実行

不可(以下のエラーが出力されます)

Coroutine couldn’t be started because the the game object ‘〜〜〜’ is inactive!

~~~ にはGameObject名が入ります

まとめ

GameObject、Componentが非アクティブだとしても関数の実行は可能だったようです。

ただコルーチンはGameObjectがアクティブではないと実行はされません

この結果から分かることは、Update等のイベント以外で、関数の呼び出しを停止したいからGameObject、Componentを非アクティブにするといったことは全くもって見当違いということです。

もし独自マネージャクラスでUpdateを呼び出しているのであれば、マネージャ側が対象インスタンスのアクティブをチェックして実行可否を判断しなければならない場合もあるかもしれません。もちろん非アクティブのときもUpdateしたいよっていうこともあると思うので、仕様次第かと。

public class UpdateManager : MonoBehaviour
{
    private List<IUpdatable> _updateObjList;

    void Update ()
    {
        foreach(var obj in _updateObjList)
        {
            // オブジェクトのアクティブをチェックしてUpdate処理をする必要があるかもしれない
            if (obj.IsActive)
            {
                obj.CustomUpdate();
            }

GameObject、コンポーネントのアクティブ切り替えをする主な目的の1つは無駄な描画をさせないためだと思います。

例えばuGUIのImageコンポーネントは、Graphicクラスを継承しており、OnPopulateMesh というイベント処理によって画面にメッシュを描画しています。

このイベントもGameObject、コンポーネントが非アクティブになると実行されないため描画されなくなります。この切替で描画のパフォーマンスチューニングの1つの手段になるかと思います。

GameObjectとコンポーネントのアクティブ切り替え話を延々してきました。

結局SetActiveとenabledどっちを使えばよいのか

  • コンポーネント のアクティブ切り替えは(enabled) イベントの実行 / 停止 を基準に判断
  • GameObject のアクティブ切り替え(SetActive)も イベントの実行 / 停止 が基準になるが、コルーチン が絡む場合は使わない。コルーチンを止めたい場合は使うかもしれない。

という着地です。

とりあえず非アクティブにしたいから何でもかんでもSetActiveを使っていた時代が私にはありましたが、今は上記のことを意識して切り替えています。

合わせてどうぞ

www.shibuya24.info

www.shibuya24.info

www.shibuya24.info

www.shibuya24.info