Unity5.6のわりと地雷なバグ

お知らせ


Unity5.6が本日(日本時間4/1。北米時間の3/31)にリリースされました。
ですが、Unity5.6はβ段階で判明しているバグが未修正のままリリースされてしまっています。
詳しくは、リリースノートのKnown Issues(既知の問題)を確認してもらえればと思います。
未修正の問題があったままリリースされること自体は珍しくないのですが、Unity5.6ではわりと地雷なバグがあるので、情報共有としてここでまとめておこうかと思います。

TransformからRectTransformへの自動変換の際に、そのオブジェクトのヒエラルキーの全オブジェクトにOnDisable→OnEnableが呼ばれる

これはすでに、UnityのIssuTrackerでも報告されています。
また、リリースノートの「Known Issues」にも以下のように記述があります。
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)

具体的にどういうケースで発生するのか

よくあるコードだと思うのですが、これだけでバグが発生します。

Unityのオブジェクトは通常はTransformというコンポーネントを必ず持っているのですが、UIを使うときだけRectTransformという派生コンポーネントを使います。
このTransformからRectTransformへの変更は自動変換されるのですが、
Unity5.6からはこの自動変換の時に「コンポーネントに対してOnDisableとOnEnableが呼ばれるバグ」が発生します。
しかも影響範囲は、そのオブジェクトを含むヒエラルキーの全オブジェクト。つまり全ての親子オブジェクトになるようです。
(ただし、最初から非アクティブのオブジェクトは関係ないようです)
バグと言っても、エラーメッセージもWarningも出ないため密かに全体を破壊していきます。

バグによって起きる問題

コルーチンが勝手に止まる

一度、OnDisableになるということはコルーチンが止まります
しかも、同じヒエラルキーの全オブジェクト、全コンポーネントに対してです。
根本的な仕組みが破壊されてしまうので、ゲームそのものが全く動かなくなる危険が高いです。




追記:スクリプトのenableが→オフ→オンじゃなくて、GameObject自体のactiveが→オフ→オンとなってしまう?
テラシュールさんが補足記事を書いてくれました
Unityの仕様上、OnDisableになることだけではコールチンは止まらないようです。
ですが、このバグでコルーチンは止まります。
どうも、スクリプトのenableが→オフ→オンじゃなくて、GameObject自体のactiveが→オフ→オンとなってしまうと言うのが、正確なところのようです。

再現コードはこんな感じ。

意図しない挙動が起きる

他にも、OnDisableやOnEnableに処理を記述していた場合はそれが勝手に呼ばれます。
たとえば、OnDisableやOnEnableをUI画面の開閉のトリガーとして使っていた場合は、
「子オブジェクトにUIを追加したら、勝手に画面が閉じて開く処理が走って初期状態にもどる」・・・みたいな動作をすることになるかと思います。
IssuTrackerにあるように、最悪クラッシュすら起きる可能性があります。

バグの原因特定がとても難しくなる

このバグの一番やっかいな点は、AddComponentしたオブジェクトだけではなく、同じヒエラルキーの「全オブジェクト」の「全コンポーネント」に影響する点です。
エラーメッセージもWarningも出ないため、「いったいどのコードでバグが起きたのか?」が分かりづらくなってしまいます。
しかもコルーチンが止まるなどの、時間差がある動作にも影響するため、「いつバグが起きたのか?」すらもわかりづらくなります。
このバグの存在を知っていればまたマシですが、知らない場合は何が原因か特定するのは非常に困難です。
知っていたとしても、自分だけではなくプロジェクトの参加者の全コード、使用しているアセットの全コードでそのような記述がないか、あった場合にそれが原因で不具合がおきる可能性があるかをチェックするのは現実的には不可能でしょう。
つまり、強烈な地雷バグです。

対処方法

公式の記述にあるように、
new GameObject("Canvas", typeof(RectTransform));
として、GameObjectを生成する際に第二引数にtypeof(RectTransform)を追加することです。
この方法なら最初からTransformではなくRectTransformを持ったオブジェクトが作成されるので、RectTransformへの自動変換が起きずにバグは発生しません。
また、GameObjectをヒエラルキーに入れる前(SetParentする前)に、UI系のコンポーネントをAddComponentすればヒエラルキー内の他のオブジェクトへバグが影響することもないようです。
ただし、自分の書いたコードならまだしも、アセットストアのアセットの場合は修正が困難かと思います。
そもそもが仕様ではなくバグなので、下手にコードをいじらずに、バグが修正されるまではUnity5.6を使わないほうがいいかもしれません。

Canvasを入れ子で使っていると、アクティブのオン・オフを切り替えると入力が利かなくなる

このバグは、Unity5.6.0p3で修正されたようです。
こちらも、UnityのIssuTrackerでも報告済みです。
あまり知られていませんが、Canvasは入れ子で使うことができます。
ですが、Unity5.6ではCanvasを入れ子にして使っていると、オブジェクトのアクティブのオンオフを切り替えると、入力が利かなくなるバグがあります。

そもそもCanvasの入れ子って使わなければいいのでは?

確かにCanvasの入れ子は使わなくても良いのですが、Unity公式で使用を推奨しています。
チュートリアル>ベストプラクティス>Fill-rate, Canvases and inputの「Splitting Canvases」の項目です。

これはどういうものかというと、UIの負荷軽減のためものです。
Canvasは再構築の負荷が高いので、動くUIなどは負荷が高くなってしまいます。
なので、Canvasを入れ子にして、動くものと動かないものなどを分けることで負荷を軽減できるというテクニックです。
Unity道場のこちらのスライドが分かりやすいかと思います。

ですが、Unity5.6ではこの手法を使っているとUIへの入力が利かなくなるというバグが発生します。
しかも、必ず起きるわけではなく、特定の条件でのみ発生するようです。(条件はわかりませんでした)
CanvasがWold設定だと起きたようです。
ちなみに、このバグへの応急対応として、キャンバスを入れ子にしないようにするには、当然シーンの構成を変える必要があります。
アセットストアでアセットを配布してる身としては、(アセットの使用者全員のプロジェクトの修正ができるわけもないので)スクリプトで対応できない不具合はお手上げになってしまうので非常に困っています。
Canvasの入れ子は以前から不安定な部分だったので個人的には使用を避けていたのですが、「もう公式で推奨しているし大丈夫じゃないか」という話を聞いて使ってみたのですが・・・どうもまだやっぱり不安定なようです。

今できること

上記のケースに該当する恐れのあるプロジェクトでは、下手に応急処置をするよりはUnity5.6は使用しないのが一番かと思います。
ただ、待っているだけだと別の未知の不具合の発見が遅れる恐れがありますので、自分のプロジェクトを試してみて動かないようなら調査してバグレポートを報告したほうが良いかと思います。
また、IssueTrackerに投票して修正の優先順位を少しでも上げましょう。

RectTransform変換のバグに関してのIssueTrackerはこちらです
Canvas入れ子のバグに関してのIssueTrackerはこちらです

IssueTrackerはUnity公式の「既知の不具合のリスト」のようなもので、投票が多いものほど優先的に修正されるようです。
バグが放置されているときは、開発者が「そんなに重要なバグではない」と思っているケースも多々あります。
なので、使用者側から積極的に声を上げて、困っている人が多いということを知らせていきましょう。
個人的には、Canvas入れ子のバグに関してのIssueTrackerのほうが困っているので、こちらにぜひ投票していただけると助かります。

« »