こんにちわ、Unityエンジニアのオオバです。

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

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

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

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

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

アクティブ状態というは👆上のとおり、
GameObjectとコンポーネントにチェックが入った状態のことです。

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

この記事の内容

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

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

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

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

が呼ばれます。

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

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

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

が呼ばれます。

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

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

が呼ばれす。

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

が呼ばれます。

ここまでのまとめ その1

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

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

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

Start 1  
Update 1  
Update 2  
Start 2  

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

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

Start 1  
Update 1  
Start 2  
Update 2  

理由は公式マニュアルに記載されていました。
イベント関数の実行順序 - Unity マニュアル

正解はこの一文です。

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

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

ここまでのまとめ その2

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

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

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

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

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

ここまでのまとめ その3

この結果から分かること

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

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

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

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

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

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

SetActiveの使用判断

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

enabledの使用判断

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

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

最後に

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

オススメ記事
2021秋 Asset Refreshセール
100以上のアセットがなんと50%OFF!!オオバもいくつか買いました!
期間 : 10月2日午後3時59分まで
検証環境