Unity 的 null
本课属于 Coddy Unity 学习旅程的“Unity 脚本基础”单元。第 60 课,共 85 课。
Unity 对象有两部分:脚本持有的 C# 对象,以及它背后的引擎对象。Destroy(obj) 会在帧结束时移除引擎对象,但你的变量仍然指向 C# 对象,所以从纯 C# 的角度来看,它不为 null。Unity 重载了 == 来处理这种情况:对象销毁后,obj == null 为 true,简短的测试 !obj 也是如此。这就是 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 的对象(GameObject、组件、你的脚本),都要使用 == null、!= null 或 if (obj) 进行检查;对于字符串等普通 C# 对象,则使用 ?. 和 ??。还要记住,Destroy 会等到帧结束才生效:在此之前,对象仍然存活,== null 的结果为 false。
挑战
中等目标被销毁后,停止无人机产生的错误。
- 在
Update()中,将两行都替换为if (target != null),这是 Unity 自带的空值检查。 - 在其中写入
transform.position = Vector3.MoveTowards(transform.position, target.position, speed * Time.deltaTime);。
按“开始”:无人机追逐目标,然后在控制台中安静等待,不再出现错误。然后点击“检查”。
亲手试试
using UnityEngine;
public class Chaser : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private float speed = 2f;
void Start()
{
Destroy(target.gameObject, 2f); // 2 秒后销毁 target
}
void Update()
{
Vector3 goal = target?.position ?? transform.position;
transform.position = Vector3.MoveTowards(transform.position, goal, speed * Time.deltaTime);
}
}
Unity 脚本基础中的所有课
自己练习: Unity 在线编辑器