UnityのNull
CoddyのUnityジャーニー「Unityスクリプティングの基礎」セクションの一部。レッスン 46/67。
Unityのオブジェクトには2つの側面があります。スクリプトが保持するC#オブジェクトと、その背後にあるエンジンオブジェクトです。Destroy(obj)はフレームの終わりにエンジン側を削除しますが、変数は依然としてC#側を指しているため、通常のC#の観点ではnullではありません。Unityはこれに対応するために==をオーバーライドしています。オブジェクトが破棄されると、obj == nullはtrueになり、短い判定の!objもtrueになります。これがUnityの偽のnullです。
?.、??、is nullは、その==を使用しません。これらはC#の参照をテストしますが、その参照はまだ存在するため、呼び出しは実行され、破棄されたオブジェクトにアクセスするとMissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.がスローされます。このラベルには、ノックアウト(破棄)されることがあるレースの先頭を走る者の名前が表示されます。
[SerializeField] private Transform leader;
void Update()
{
// leader が破棄されると例外が発生する: string shown = leader?.name ?? "nobody";
string shown = leader != null ? leader.name : "nobody";
Debug.Log("Leader: " + shown);
}UnityEngine.Objectに由来するもの(GameObjects、コンポーネント、自分のスクリプト)には、== null、!= null、またはif (obj)で判定し、文字列などの通常のC#オブジェクトには?.と??を使いましょう。また、Destroyはフレームの終わりまで待機することも覚えておきましょう。それまではオブジェクトはまだ生きており、== nullはfalseです。
チャレンジ
中級ドローンはターゲットを追跡しますが、ゲーム開始から2秒後にターゲットは破棄されます。その後、ConsoleにMissingReferenceExceptionが表示され続けます。これは、target?.positionではターゲットが消えたことを検知できないためです。
Unity独自のnull判定を使ってUpdate()を修正しましょう。ターゲットが存在する間は、ドローンはspeedでターゲットに向かって飛びます。ターゲットが破棄された後は、エラーを出さず、その場にとどまります。
Playを押してConsoleを確認し、スクリプトを修正してからCheckを押しましょう。
自分で試してみよう
using UnityEngine;
public class Chaser : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private float speed = 2f;
void Start()
{
Destroy(target.gameObject, 2f); // targetは2秒後に破棄される
}
void Update()
{
Vector3 goal = target?.position ?? transform.position;
transform.position = Vector3.MoveTowards(transform.position, goal, speed * Time.deltaTime);
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。