[SerializeField] private GameObject target; // 参照している。nullじゃない。
private void Awake()
{
Debug.Log(target == null); // Falseと表示される
DestroyImmediate(target);
Debug.Log(target == null); // Trueと表示される
}
上のコードは、Unityにいくつかある「はまりポイント」や「なんじゃこりゃってなるポイント」の一つです。
変数target
がオブジェクトを参照していてnull
じゃないのに、target == null
がtrue
。
解説
GameObject
やComponent
を==
オペレーターを使って、target == null
のように評価する際、変数がオブジェクトを参照しているのにも関わらず、true
と評価されることがあります。
これらの親クラスであるUnityEngine.Object
には、==
や!=
、bool
のオペレーターが定義されています。
そして、その挙動が「対象のGameObject
やComponent
が、生存していないならば、==
でnull
と比較するとtrue
を返す」という仕様になっています
内部でこんなメソッドを作って生存しているかチェックをしています。
- IsNativeObjectAlive
- DoesObjectWithInstanceIDExist
この挙動は、私はあまり好きではありません。「コードは、利用者の期待や予測に対して、驚きが最小であるべき」と思っています。この挙動は、==
に対して利用者が予期しない「生存チェックもする」という驚きを与えてしまっています。
覚えておかないと、予期せぬバグを発生させることがあるので、注意が必要ですね。(多分、ごくまれに発生するめんどいバグを生み出す原因になりそう)
次のコードを見てみることもオススメします。
おまけ。対応策
変数が参照するオブジェクトが破壊されるケースがあり、純粋にnullと比較したい場合System.Object.ReferenceEquals
を使うことをオススメします。
[SerializeField] private GameObject target; // 参照している。nullじゃない。
private void Awake()
{
Debug.Log(target == null); // Falseと表示される
Debug.Log(ReferenceEquals(target, null)); // Falseと表示される
DestroyImmediate(target);
Debug.Log(target == null); // Trueと表示される
Debug.Log(ReferenceEquals(target, null)); // Falseと表示される
}