【Unity】「Unity5.6のわりと地雷なバグ」についての補足
下で紹介された内容は、
どちらも解決したみたいです。*1
先日リリースされた「Unity 5.6」について、幾つか地雷なバグがあるそうなので見てみましたが、内容が少し気になったので調べてみました。
AddComponentでImageを足した際に、OnDisable→OnEnableが呼ばれる
これはKnown Issueにある通り、発生します。
UI: Adding UI elements via script causes OnDisable and OnEnable to be called on all scripts in the hierarchy if a RectTransform needs to be added. Workaround is to add a RectTransform component in the new GameObject call, for example: new GameObject("Canvas", typeof(RectTransform));. (882791)
これは正確には、以下の条件で発生します。
- GameObjectを「new GameObject("name")」のAPIで、GameObjectを動的に生成する
- RectTransformを要求するコンポーネントを AddComponentで追加する
(例えばImageやButton等)
上の条件で、GameObjectに設定されていたTransformをRectTransformに変換する処理が走り、その際にOnDisable->OnEnableが呼ばれるみたいです。
特に破棄系の初期化処理を記述していた場合、ややこしい問題です。
Prefabを置くタイプなら問題ない
つまり、AddComponentを使用しなければ起こりませんし、AddComponentを使用した場合でもRectTransformが既にあれば初期化は発生しないという事みたいです。
例えばPrefabを置くタイプのように、既にRectTransformが設定されているオブジェクトを取得し生成する場合、今回紹介するような不具合は発生しないみたいです。
AddComponentは遅い
ちなみに、意外と知られていないようですがAddComponentは非常に遅いAPIです。
例えば下のように、GameObjectの生成に600ms近くかかった場合、その400msはAddComponentの処理時間です。
実際、GameObjectに3つのコンポーネントを付けたオブジェクトを300個作った場合と、Prefabで読み出した場合では、Prefabから複製した方が早いです。
バグ環境下でAddComponentしたコ犯人ンポーネントを見つける
今回の不具合でどのコンポーネントがAddComponentやってるかを見つける方法です。下のコードをRootのCanvasに貼り付けると、AddComponentしたオブジェクトをスタックトレースで追えます。
上の画像では、SomコンポーネントのStartメソッド16行目でAddComponentが行われています。
コルーチンが止まる…というよりはリセットするコードが呼ばれる可能性がある
さて、今回の不具合に「コルーチンが止まるという」という情報が含まれていますが、これは補足が必要です。
コルーチンを止めるアイディアは以下の3つです。
- StopCoroutineを実行
- GameObjectを破棄する
- GameObjectを非アクティブにする
今回の不具合ではこの条件を満たせないですし、実際試した感じ止まらなかったので、これが直接的な要因ではないと思われます。
むしろ、OnDisableにコルーチン停止コードが含まれていた場合にコルーチンを停止、直後にOnEnableに記述したコルーチン起動コードで再起動という流れが出来てしまうのが問題なんだと思います。
再度確認したところ、GameObjectが一旦非アクティブになるみたいです。ので、コルーチンは止まります。
まぁ、下のような中断に対応したコルーチンなら大丈夫だと思いますが、中断に対応していないコルーチンでは問題が起こるかもしれません。
Canvasを入れ子で入力が利かなくなる、はGraphicsRaycasterが無い為
「Canvasを入れ子で使っていると、アクティブのオン・オフを切り替えると入力が利かなくなる」ですが、これは正しい動作だったと認識しています。
というか、確かCanvasを入れ子にしたい場合、Graphics RaycasterをCanvas毎に設定する必要があったはずで、むしろアクティブを切り替える等以前に何故反応するのかという気がしなくもないです(実際、エディターで確認すると反応したりしなかったりします)
パっと見気づきにくい挙動なので、もしかしたら修正されるかもしれませんが、果たして。
一応、入れ子Canvasを設定した状態でのボタンは、エディター上なら動作(何故か)する事がありますが、ビルドしたコンテンツではGraphics Raycasterが無いと反応しなかったと記憶しています。
感想
5.6でまだ治ってない、 AddComponentとRectTransformのバグについてでした。
修正と回避策自体は非常にシンプルですが、DLLにコンパイル済みのコードやAssetStoreから提供されている他の人が作ったコード等、修正しにくいプロジェクトがあるのも事実です。趣味でやってる場合この不具合が怖いなら待つのは正しい選択かもしれません。
関連
*1:Graphics Raycasterで毎回全Graphicsを精査するのは凄い無駄だと思うんですけどね…