Menu
CoddyTech

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。

challenge icon

挑战

中等

目标被销毁后,停止无人机产生的错误。

  1. 在 Update() 中,将两行都替换为 if (target != null),这是 Unity 自带的空值检查。
  2. 在其中写入 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 在线编辑器