NullReferenceException in Unity


11

Poiché molti utenti si trovano ad affrontare l' NullReferenceException: Object reference not set to an instance of an objecterrore in Unity, ho pensato che sarebbe una buona idea raccogliere da più fonti alcune spiegazioni e modi per correggere questo errore.


Sintomi

Ricevo l'errore di seguito che appare sulla mia console, cosa significa e come posso risolverlo?

NullReferenceException: riferimento all'oggetto non impostato su un'istanza di un oggetto


Sembra una domanda di programmazione generale e non specifica per gli sviluppatori di giochi. La risposta di OP alla propria domanda include un collegamento a SO che tratta questo argomento.
Pikalek,

3
Mentre "NullReferenceException" è, in effetti, una domanda di programmazione generale, qui la domanda copre specificamente l'eccezione in Unity : dove può essere incontrata nella programmazione di Unity e come risolverli (vedere i vari esempi).
Hellium,

@Pikalek, abbiamo anche ampliato la nostra portata per ciò che consentiamo in termini di programmazione generale. Questo è stato chiarito quando l'ho chiesto in meta . Mi rendo conto, ora, che questo potrebbe ancora adattarsi ai parametri di "troppo generico", secondo la risposta di Josh.
Gnemlock,

La risposta corrente non fa inoltre alcuna nota su qualcosa di specifico di Unity (tranne l'uso di tipi specifici di Unity negli esempi). È, in effetti, una risposta di programmazione generica. Non usiamo le risposte in argomenti ravvicinati, ma dato che si tratta di una risposta autonoma, va a sostenere l'argomento dell'intenzione.
Gnemlock,

3
Unity ha alcuni modi unici / caratteristici per innescare questi errori, attraverso campi Inspector non assegnati, tentativi GetComponent o Find non riusciti, o attraverso il suo aroma variante "MissingReferenceException" quando si aveva un riferimento valido ma Destroy () ed. Quindi, penso che le risposte a questa domanda nel contesto di Unity abbiano un buon potenziale per essere utili alla comunità, anche se l'eccezione stessa è molto generale.
DMGregory

Risposte:


14

Tipo di valore vs Tipo di riferimento

In molti linguaggi di programmazione, le variabili hanno quello che viene chiamato un "tipo di dati". I due tipi di dati principali sono tipi di valore (int, float, bool, char, struct, ...) e tipo di riferimento (istanza di classi). Mentre i tipi di valore contengono il valore stesso , i riferimenti contengono un indirizzo di memoria che punta a una porzione di memoria allocata per contenere un set di valori (simile a C / C ++).

Ad esempio, Vector3è un tipo di valore (una struttura contenente le coordinate e alcune funzioni) mentre i componenti collegati a GameObject (inclusi gli script personalizzati che ereditano da MonoBehaviour) sono di tipo di riferimento.

Quando posso avere una NullReferenceException?

NullReferenceException vengono generati quando si tenta di accedere a una variabile di riferimento che non fa riferimento a nessun oggetto, quindi è nulla (l'indirizzo di memoria punta a 0).

Alcuni luoghi comuni a NullReferenceExceptionsaranno sollevati:

Manipolazione di un GameObject / Component che non è stato specificato nella finestra di ispezione

// 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();
}

Recupero di un componente che non è collegato a GameObject e quindi, cercando di manipolarlo:

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 ;
}

Accesso a un GameObject che non esiste:

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";
}

Nota: Fate attenzione, GameObject.Find, GameObject.FindWithTag, GameObject.FindObjectOfTyperestituire solo gameObjects che sono abilitati nella gerarchia quando viene chiamata la funzione.

Cercare di utilizzare il risultato di un getter che sta tornando 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.

Accesso a un elemento di un array non inizializzato

private GameObject[] myObjects ; // Uninitialized array

private void Start()
{
    for( int i = 0 ; i < myObjects.Length ; ++i )
        Debug.Log( myObjects[i].name ) ;
}

Meno comune, ma fastidioso se non lo sai sui delegati 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) ;
}

Come risolvere ?

Se hai compreso i paragrafi precedenti, sai come correggere l'errore: assicurati che la tua variabile faccia riferimento (indicando) un'istanza di una classe (o che contenga almeno una funzione per i delegati).

Più facile a dirsi che a farsi? Si Certamente. Ecco alcuni suggerimenti per evitare e identificare il problema.

Il modo "sporco": il metodo try & catch:

Collider collider = gameObject.GetComponent<Collider>();

try
{
    collider.enabled = false ;
}       
catch (System.NullReferenceException exception) {
    Debug.LogError("Oops, there is no collider attached", this) ;
}

Il modo "più pulito" (IMHO): il controllo

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) ;
}

Di fronte a un errore che non puoi risolvere, è sempre una buona idea trovare la causa del problema. Se sei "pigro" (o se il problema può essere risolto facilmente), usa Debug.Logper mostrare sulla console le informazioni che ti aiuteranno a identificare ciò che potrebbe causare il problema. Un modo più complesso è usare i Breakpoint e il Debugger del tuo IDE.

L'uso Debug.Logè abbastanza utile per determinare quale funzione viene chiamata per prima, ad esempio. Soprattutto se si dispone di una funzione responsabile dell'inizializzazione dei campi. Ma non dimenticare di rimuoverli Debug.Logper evitare di ingombrare la tua console (e per motivi di prestazioni).

Un altro consiglio, non esitate a "tagliare" le chiamate di funzione e aggiungere Debug.Logper effettuare alcuni controlli.

Invece di :

 GameObject.Find("MyObject").GetComponent<MySuperComponent>().value = "foo" ;

Fare questo per verificare se tutti i riferimenti sono impostati:

GameObject myObject = GameObject.Find("MyObject") ;

Debug.Log( myObject ) ;

MySuperComponent superComponent = myObject.GetComponent<MySuperComponent>() ;

Debug.Log( superComponent ) ;

superComponent.value = "foo" ;

Anche meglio :

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 ) ;
}

fonti:

  1. http://answers.unity3d.com/questions/47830/what-is-a-null-reference-exception-in-unity.html
  2. /programming/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it/218510#218510
  3. https://support.unity3d.com/hc/en-us/articles/206369473-NullReferenceException
  4. https://unity3d.com/fr/learn/tutorials/topics/scripting/data-types

Ciò comporta un grande sforzo per spiegare il "come fare" della diagnosi del problema. Non ritengo che una vera risposta alla domanda "qual è il problema". Anche questo non riesce a rispondere alle risposte che appaiono in genere su questo tipo di domande. Forse questo sarebbe meglio nella documentazione StackOverflow? Forse no.
Gnemlock,

2
Non direi che usare il registro di debug sia pigro . Per me, è molto più veloce usare debug.log per restringere l'ambito in cui si sta verificando l'errore, quindi usare il debugger per trovare davvero l'errore. Ma dipende sempre dall'errore a portata di mano. In ogni caso, non direi che usare il registro di debug è pigro : P
Vaillancourt

Avresti dovuto anche sottolineare che non è sempre una buona idea mettere i controlli per null. Un'idea ancora peggiore sarebbe quella di usare try/catch. L'errore ti dice molto sul problema che hai lì e prima che i principianti inizino a mettere controlli null ovunque, il tuo problema principale è nell'ispettore poiché ti dimentichi di fare riferimento a un oggetto (trascina l'oggetto sullo script). Ho visto un sacco di codice con try/catche controlli null in luoghi in cui è totalmente inutile. Eseguire il debug e lavorare con un codice del genere è "una seccatura in a **". I principianti imparano i casi d'uso di tali controlli e solo successivamente li usano.
Candid Moon _Max_

Penso che avere un controllo nullo possa essere una buona idea se un messaggio di debug esplicito è fornito in else. Avere un NullReferenceExceptionnon è sempre autoesplicativo mentre No Rigidbody component attached to the gameObjectspiega direttamente cosa non va. Concordo sul fatto che avere il if( obj != null )messaggio senza "nasconde" il problema, e puoi avere un progetto funzionante ma non fare quello che ti aspetteresti senza sapere il perché.
Hellium,

4

Mentre possiamo facilmente fare un controllo per assicurarci che non stiamo provando ad accedere a un riferimento null, questa non è sempre una soluzione adatta. Molte volte, nella programmazione di Unity, il nostro problema potrebbe derivare dal fatto che il riferimento non dovrebbe essere nullo. In alcune situazioni, ignorare semplicemente i riferimenti null può violare il nostro codice.

Ad esempio, potrebbe essere un riferimento al nostro controller di input. E 'bello che il gioco non va in crash a causa l'eccezione di riferimento nullo, ma abbiamo bisogno di capire il motivo per cui non v'è alcun controller di input, e correzione che problema. Senza di essa, abbiamo un gioco che potrebbe non arrestarsi in modo anomalo, ma non può accettare input.

Di seguito elencherò i possibili motivi e soluzioni, mentre li incontro in altre domande.


Stai provando ad accedere a una classe "manager"?

Se stai tentando di accedere a una classe che funge da "manager" (ovvero una classe che dovrebbe sempre avere solo un'istanza in esecuzione alla volta), potresti stare meglio usando l'approccio Singleton . Una classe Singleton è idealmente accessibile da qualsiasi luogo, direttamente, mantenendo un public staticriferimento a se stessa. In questo modo, un Singleton può contenere un riferimento all'istanza attiva, che sarebbe accessibile senza il problema di impostare il riferimento effettivo ogni volta.

Stai facendo riferimento all'istanza del tuo oggetto?

È comune contrassegnare semplicemente un riferimento come public, così possiamo impostare il riferimento all'istanza tramite l'ispettore. Verifica sempre di aver impostato il riferimento a un'istanza, tramite l'ispettore, poiché non è raro perdere questo passaggio.

Stai istanziando la tua istanza?

Se stiamo impostando il nostro oggetto nel codice, è importante assicurarci di creare un'istanza dell'oggetto. Ciò può essere effettuato utilizzando la newparola chiave e i metodi di costruzione. Ad esempio, considerare quanto segue:

private GameObject gameObject;

Abbiamo creato un riferimento a GameObject, ma non punta a nulla. L'accesso a questo riferimento così com'è comporterà un'eccezione di riferimento null . Prima di fare riferimento alla nostra GameObjectistanza, possiamo chiamare un metodo di costruzione predefinito come segue:

gameObject = new GameObject();

Il tutorial Unity sulle classi spiega la pratica di creare e usare i costruttori.

Stai usando il GetComponent<t>()metodo presupponendo che il componente esista?

Innanzitutto, assicurati di chiamare sempre GetComponent<t>()prima di chiamare i metodi dall'istanza del componente.

Per motivi che non valgono la pena, possiamo supporre che il nostro oggetto di gioco locale contenga un particolare componente e proviamo ad accedervi GetComponent<t>(). Se l'oggetto di gioco locale non contiene quel particolare componente, restituiremo un nullvalore.

Puoi facilmente verificare se il valore di ritorno è null, prima di accedervi. Tuttavia, se l'oggetto di gioco deve avere il componente richiesto, potrebbe essere meglio assicurarsi che abbia almeno una versione predefinita di quel componente. Possiamo taggare un MonoBehaviouras [RequireComponent(typeof(t))]per assicurarci di avere sempre quel tipo di componente.

Ecco un esempio di a MonoBehaviourper un oggetto di gioco che dovrebbe sempre contenere a Rigidbody. Se lo script viene aggiunto a un oggetto gioco che non contiene un Rigidbody, Rigidbodyverrà creato un valore predefinito .

[RequireComponent(typeof(Rigidbody))]
public class AlwaysHasRigidbody : MonoBehaviour
{
    Rigidbody myRigidbody;


    void Start()
    {
        myRigidbody = GetComponent<Rigidbody>();
    }
}

Hai provato a ricostruire il tuo progetto?

Ci sono alcuni casi in cui Unity può causare problemi provando a fare riferimento a una versione cache di un oggetto gioco. In linea con l'antica soluzione "Spegni e riaccendi", prova a eliminare la cartella Libreria e riapri Unity. Unity sarà costretto a ricostruire il tuo progetto. Questo può risolvere alcuni casi molto particolari di questo problema e dovrebbe puntare a problemi che non emergerebbero in una build finale.


1
Non sono ancora sicuro se questa domanda dovrebbe essere in tema. Ma ecco una wiki della community per gli utenti che possono pubblicare ulteriori potenziali risposte; Finora comprende le basi della prima mezza pagina di risposte accettate per le domande contrassegnate come unità e "riferimento null" (che soddisfano effettivamente i criteri della domanda).
Gnemlock,

-5

Vedo che c'è una risposta accettata. Tuttavia, esiste una risposta o un suggerimento migliore per la gestione NullReferenceException. Se riesci a mettere in relazione la programmazione in linguaggio Java come me, puoi impedire di inviare un errore null utilizzando il try-catchblocco. Provalo tu stesso! ;-)

Se stai usando in C #, controlla se hai using System;nella parte superiore del file di script. In caso contrario, aggiungilo. Ora puoi usare tutti i tipi di Exceptionclassi mentre cerchi di catturare una riga di codice.

Se stai usando UnityScript, usa import System;

Ecco un esempio:

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

        }

    }
}

Ricorda inoltre, si può prendere anche altre eccezioni come MissingReferenceException, MissingComponentException, IndexOutOfRangeException, o altri classi di eccezioni, purché si includere using Systemnello script.

Questo è tutto.


2
Il metodo try & catch è stato descritto nella risposta accettata ....
Hellium,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.