渋谷ほととぎす通信

新しいこと、枯れたこと問わず大庭が興味を持ったものを調査、生活の効率を求める完全趣味の技術ブログ。基礎を大事にしています。

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


f:id:esakun:20150730215258g:plain:w450

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

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

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

これらの関数が止まることなく実行される条件は、「GameObject、アタッチされたコンポーネントが共にアクティブ状態の場合」です。

f:id:esakun:20161027215320p:plain
アクティブ状態とは、上図のように、GameObject、コンポーネントにチェックが入った状態のことです。

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

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

前提としてシーンロード時にGameObject、コンポーネント共に非アクティブの状態とします。ちなみにこの状態では、どのイベントも実行されません。

下記パターンごとに実行されるイベントとその順を記載しています。

◆パターン1. GameObject アクティブ : コンポーネント 非アクティブ
  1. Awake

が呼ばれます。

◆パターン2. GameObject 非アクティブ : コンポーネント アクティブ

何もイベントは呼ばれません

◆パターン3. GameObject アクティブ : コンポーネント アクティブ
  1. Awake
  2. OnEnable
  3. Start
  4. Update

が呼ばれます。


ここまでを表にしてみます。

パターン GameObject コンポーネント 実行イベント
1 アクティブ 非アクティブ Awake
2 非アクティブ アクティブ 無し
3 アクティブ アクティブ Awake, OnEnable, Start, Update
◆補足1. パターン1の後、コンポーネント アクティブに切り替える
  1. OnEnable
  2. Start
  3. Update

が呼ばれす。

◆補足2. 補足1.の後、GameObject or コンポーネントを非アクティブにしてアクティブに戻す
  1. OnEnable
  2. Update

が呼ばれます。
※GameObject、コンポーネントともに同じ結果になりました

ここまでのまとめ その1

  • AwakeStartイベントはインスタンス生成後1度しか実行されないイベント
  • OnEnableイベントはアクティブの切り替えがトリガーで実行されるイベント
  • GameObject、コンポーネントをどちらか非アクティブにするとUpdate停止する
◆補足3. パターン3においてStartにコルーチン使った場合

以下のような場合どのようなログが出力されるでしょうか。

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

Start 1
Update 1
Update 2
Start 2

処理順番としてはStart -> Updateですが、Start関数が遅延実行の場合、Start関数処理実行中にUpdateは処理されることになります。

しかし、Updateが2回呼ばれた後、1フレーム待機してStart 2が実行されることに疑問が湧いてきます。予測されるログは以下のようなイベント順なのではないかと。

Start 1
Update 1
Start 2
Update 2

理由は公式マニュアルに記載されていました。

正解はこの一文です。

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

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

ここまでのまとめ その2

  • GameObjectが非アクティブの場合はコンポーネントがアクティブになっても何もイベントは走らない
  • Awakeは例外でコンポーネントではなくGameObjectのアクティブがトリガーで1度だけ実行される

非アクティブの状態におけるユーザー定義関数実行検証

少し視点を変えて、非アクティブの状態における、ユーザー定義の関数実行挙動も確認していきます。

パターン GameObject コンポーネント ユーザー関数 コルーチン
1 アクティブ 非アクティブ 実行可能 実行可能
2 非アクティブ アクティブ 実行可能 不可
3 アクティブ アクティブ 実行可能 実行可能
4 非アクティブ 非アクティブ 実行可能 不可

コルーチンが実行不可の場合、以下のエラーが出力されます。

Coroutine couldn't be started because the the game object 'GameObject名' is inactive!

ここまでのまとめ その3

  • GameObject、コンポーネントが共に非アクティブ状態でも、ユーザー定義関数の実行は可能
  • コルーチンはGameObjectがアクティブではないと実行出来ない

この結果から分かること

Unityが定義したイベント(Update等)ではない、ユーザー定義関数の呼び出しを止めるためにGameObjectやコンポーネントを非アクティブにするということは無意味です。

GameObject、コンポーネントのアクティブ切り替えをする理由

GameObject、コンポーネントのアクティブ切り替えをする理由は様々ですが、僕が思いつくものを列挙しておきます。

  • 描画を止めるため
  • コルーチンを止めるため
  • アニメーションを止めるため(Animatorなど)
  • 物理演算を止めるため

と行ったことが考えられます。
主にCPUで処理しているものを止めるためと言って良いと思います。

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

ご存知の通りSetActiveはGameObjectのメソッド、enabledはコンポーネントのプロパティですが、以下の指針で僕は判断しています。

SetActiveの使用判断

Unityイベントの実行と停止を基準に判断しますが、CPU処理を止めたい、コルーチンを止めたい場合にも判断することになります。またGameObjectにくっついているコンポーネントを一気に非アクティブになるため、実装によってはその辺りも判断材料になります。

enabledの使用判断

Unityイベントの実行と停止を基準に判断しますが、こちらもCPU処理を止める(例えばImageコンポーネントを非アクティブにして、描画を止めるなど)場合にも判断することになります。

SetActiveとの違いは、コルーチンが絡むか絡まないかという点です。コンポーネントのアクティブを切り替えてもコルーチンの動作に影響はありません。

最後に

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

環境

  • Unity2019.1.9f1
こちらの記事もあわせてどうぞ