由于许多用户都面临NullReferenceException: Object reference not set to an instance of an object
Unity中的错误,我认为从多个来源收集一些解释和解决此错误的方法是一个好主意。
病征
我的控制台中出现以下错误,这是什么意思,该如何解决?
NullReferenceException:对象引用未设置为对象的实例
由于许多用户都面临NullReferenceException: Object reference not set to an instance of an object
Unity中的错误,我认为从多个来源收集一些解释和解决此错误的方法是一个好主意。
我的控制台中出现以下错误,这是什么意思,该如何解决?
NullReferenceException:对象引用未设置为对象的实例
Answers:
在许多编程语言中,变量具有所谓的“数据类型”。两种主要的数据类型是值类型(int,float,bool,char,struct,...)和引用类型(类的实例)。值类型包含值本身时,引用包含一个内存地址,该内存地址指向分配来包含一组值的内存部分(类似于C / C ++)。
例如,Vector3
是值类型(包含坐标和一些函数的结构),而与GameObject相连的组件(包括从中继承的自定义脚本MonoBehaviour
)是引用类型。
NullReferenceException
当您尝试访问未引用任何对象的引用变量时抛出,因此该变量为null(内存地址指向0)。
NullReferenceException
将提出一些常见的地方:
操纵未在检查器中指定的GameObject /组件
// t is a reference to a Transform.
public Transform t ;
private void Awake()
{
// If you do not assign something to t
// (either from the Inspector or using GetComponent), t is null!
t.Translate();
}
检索未附加到GameObject的组件,然后尝试对其进行操作:
private void Awake ()
{
// Here, you try to get the Collider component attached to your gameobject
Collider collider = gameObject.GetComponent<Collider>();
// But, if you haven't any collider attached to your gameobject,
// GetComponent won't find it and will return null, and you will get the exception.
collider.enabled = false ;
}
访问不存在的GameObject:
private void Start()
{
// Here, you try to get a gameobject in your scene
GameObject myGameObject = GameObject.Find("AGameObjectThatDoesntExist");
// If no object with the EXACT name "AGameObjectThatDoesntExist" exist in your scene,
// GameObject.Find will return null, and you will get the exception.
myGameObject.name = "NullReferenceException";
}
注:要当心,GameObject.Find
,GameObject.FindWithTag
,GameObject.FindObjectOfType
只返回被gameObjects 启用层次结构中,当函数被调用。
尝试使用返回的getter的结果null
:
var fov = Camera.main.fieldOfView;
// main is null if no enabled cameras in the scene have the "MainCamera" tag.
var selection = EventSystem.current.firstSelectedGameObject;
// current is null if there's no active EventSystem in the scene.
var target = RenderTexture.active.width;
// active is null if the game is currently rendering straight to the window, not to a texture.
访问未初始化数组的元素
private GameObject[] myObjects ; // Uninitialized array
private void Start()
{
for( int i = 0 ; i < myObjects.Length ; ++i )
Debug.Log( myObjects[i].name ) ;
}
不太常见,但是如果您不了解C#委托人,则会很烦人:
delegate double MathAction(double num);
// Regular method that matches signature:
static double Double(double input)
{
return input * 2;
}
private void Awake()
{
MathAction ma ;
// Because you haven't "assigned" any method to the delegate,
// you will have a NullReferenceException
ma(1) ;
ma = Double ;
// Here, the delegate "contains" the Double method and
// won't throw an exception
ma(1) ;
}
如果您已理解前面的段落,则知道如何解决该错误:确保变量引用(指向)一个类的实例(或至少包含一个代表函数)。
说起来容易做起来难吗?确实是的。这里有一些避免和识别问题的技巧。
“肮脏”的方式:try&catch方法:
Collider collider = gameObject.GetComponent<Collider>();
try
{
collider.enabled = false ;
}
catch (System.NullReferenceException exception) {
Debug.LogError("Oops, there is no collider attached", this) ;
}
“更清洁”的方式(恕我直言):检查
Collider collider = gameObject.GetComponent<Collider>();
if(collider != null)
{
// You can safely manipulate the collider here
collider.enabled = false;
}
else
{
Debug.LogError("Oops, there is no collider attached", this) ;
}
遇到无法解决的错误时,找到问题原因总是一个好主意。如果您“懒惰”(或者可以轻松解决问题),请Debug.Log
在控制台上显示信息,以帮助您确定可能导致问题的原因。一种更复杂的方法是使用IDE的断点和调试器。
例如,使用Debug.Log
确定一个函数首先调用非常有用。特别是如果您有负责初始化字段的功能。但是,请不要忘记删除它们Debug.Log
,以免使控制台混乱(出于性能原因)。
另一个建议是,不要犹豫“削减”您的函数调用,并添加Debug.Log
进行一些检查。
代替 :
GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;
这样做检查是否设置了每个引用:
GameObject myObject = GameObject.Find("MyObject") ;
Debug.Log( myObject ) ;
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
Debug.Log( superComponent ) ;
superComponent.value = "foo" ;
更好的是:
GameObject myObject = GameObject.Find("MyObject") ;
if( myObject != null )
{
MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;
if( superComponent != null )
{
superComponent.value = "foo" ;
}
else
{
Debug.Log("No SuperComponent found onMyObject!");
}
}
else
{
Debug.Log("Can't find MyObject!", this ) ;
}
try/catch
。该错误告诉您很多有关那里存在的问题的信息,在初学者开始对任何地方进行空检查之前,您的主要问题是在检查器中,因为您忘记了引用某个对象(将对象拖到脚本上)。我已经在很多try/catch
不必要的地方看到了很多带有null检查的代码。调试和使用这样的代码是“ a **中的痛苦”。初学者了解这些检查的用例,然后才使用它们。
else
。在直接解释问题的NullReferenceException
同时,拥有a 并不总是自我解释No Rigidbody component attached to the gameObject
。我同意,只有if( obj != null )
不带消息只会“隐藏”问题,并且您可以拥有一个正在运行的项目,但在不知道为什么的情况下就无法做自己希望做的事情。
尽管我们可以轻松地进行检查以确保我们不尝试访问空引用,但这并不总是合适的解决方案。很多时候,在Unity编程中,我们的问题可能源于引用不应为null 的事实。在某些情况下,仅忽略空引用可能会破坏我们的代码。
例如,它可能是对我们的输入控制器的引用。很好的是,游戏不会由于空引用异常而崩溃,但是我们需要弄清楚为什么没有输入控制器,然后解决该问题。没有它,我们的游戏可能不会崩溃,但也无法接受输入。
下面,当我在其他问题中遇到它们时,我将列出可能的原因和解决方案。
您是否要访问“经理”类?
如果您尝试访问充当“经理”的类(即,一次只能运行一个实例的类),那么使用Singleton方法可能会更好。理想情况下,通过保留public static
对自身的引用,可以从任何地方直接访问Singleton类。这样,单例可以包含对活动实例的引用,可以访问该实例,而不必每次都设置实际引用。
您是否在引用对象的实例?
通常将引用标记为public
,这很常见,因此我们可以通过检查器将引用设置为实例。经常检查你有通过检查器设置了对实例的引用,因为经常会错过此步骤。
您要实例化实例吗?
如果要在代码中设置对象,那么确保实例化该对象很重要。这可以使用new
关键字和构造函数方法来执行。例如,考虑以下内容:
private GameObject gameObject;
我们创建了对的引用GameObject
,但未指向任何内容。按原样访问此引用将导致null引用异常。在引用GameObject
实例之前,我们可以按如下方式调用默认的构造方法:
gameObject = new GameObject();
关于类的Unity教程介绍了创建和使用构造函数的实践。
您是否GetComponent<t>()
在假设组件存在的情况下使用该方法?
首先,请确保我们始终致电 GetComponent<t>()
在调用组件实例的方法之前先进行调用。
出于不值得探讨的原因,我们可以假定本地游戏对象包含特定组件,并尝试使用进行访问GetComponent<t>()
。如果本地游戏对象并没有包含具体的组成部分,我们将返回一个null
值。
null
在访问它之前,您可以轻松地检查返回值是否为。但是,如果您的游戏对象应该具有必需的组件,则最好确保它至少具有该组件的默认版本。我们可以标记为MonoBehaviour
as [RequireComponent(typeof(t))]
以确保我们始终具有该类型的组件。
这是MonoBehaviour
一个游戏对象的示例,该对象应始终包含Rigidbody
。如果将脚本添加到不包含的游戏对象中,则会创建Rigidbody
默认值Rigidbody
。
[RequireComponent(typeof(Rigidbody))]
public class AlwaysHasRigidbody : MonoBehaviour
{
Rigidbody myRigidbody;
void Start()
{
myRigidbody = GetComponent<Rigidbody>();
}
}
您是否尝试过重建项目?
在某些情况下,Unity可能会通过尝试引用游戏对象的缓存版本来引起问题。与古老的“关闭并重新打开”解决方案一致,尝试删除您的Library文件夹,然后重新打开Unity。Unity将被迫重新构建您的项目。这可以解决此问题的一些非常特殊的情况,并且应指出最终版本中不会出现的问题。
我看到有一个可接受的答案。但是,有一个更好的答案或建议供您处理NullReferenceException
。如果您可以像我这样关联Java语言的编程,则可以使用该try-catch
块来防止发送空错误。自己尝试一下!;-)
如果您使用的是C#,请检查using System;
脚本文件的顶部是否位于该位置。如果没有,请添加它。现在,您可以使用各种Exception
在尝试捕获一行代码时类。
如果您使用的是UnityScript,请使用 import System;
这是一个例子:
using System; // --> This exact line of code. That's it.
using UnityEngine;
public class Test : MonoBehaviour {
public GameObject player; // --> Example to check if there's a null content;
public void Update() {
// You may now catch null reference here.
try {
player.transform.Translate(0, 0, 2);
} catch(NullReferenceException e) { // --> You may use this type of exception class
}
}
}
还记得,你还可以赶上其他异常,如MissingReferenceException
,MissingComponentException
,IndexOutOfRangeException
,或任何其他异常类,只要你有using System
你的脚本。
就这些。