NullReferenceException in Unity

11

Karena banyak pengguna menghadapi NullReferenceException: Object reference not set to an instance of an objectkesalahan dalam Unity, saya pikir itu akan menjadi ide yang baik untuk mengumpulkan dari beberapa sumber beberapa penjelasan dan cara untuk memperbaiki kesalahan ini.


Gejala

Saya mendapatkan kesalahan di bawah ini muncul di konsol saya, apa artinya dan bagaimana cara memperbaikinya?

NullReferenceException: Referensi objek tidak disetel ke instance objek

Hellium
sumber
Ini sepertinya pertanyaan pemrograman umum & bukan game dev khusus. Jawaban OP untuk pertanyaan sendiri mencakup tautan ke SO yang mencakup topik ini.
Pikalek
3
Sementara "NullReferenceException" memang, merupakan pertanyaan pemrograman umum, di sini, pertanyaan tersebut mencakup secara khusus pengecualian di Unity : di mana dapat ditemui dalam pemrograman Unity , dan bagaimana menyelesaikannya (lihat berbagai contoh).
Hellium
@Pikalek, kami juga memperluas cakupan kami untuk apa yang kami izinkan dalam hal pemrograman umum. Ini diklarifikasi ketika saya bertanya tentang hal itu dalam meta . Saya menyadari, sekarang, bahwa ini mungkin masih sesuai dengan parameter 'terlalu umum', sesuai jawaban Josh.
Gnemlock
Jawaban saat ini juga tidak mencatat apa pun yang spesifik untuk Unity (selain menggunakan jenis spesifik Unity dalam contoh). Faktanya, ini adalah respons pemrograman generik. Kami tidak menggunakan jawaban dalam argumen dekat, tetapi mengingat itu adalah jawaban sendiri, itu pergi menuju argumen mendukung niat.
Gnemlock
3
Unity memiliki beberapa cara unik / khas untuk memicu kesalahan ini, melalui bidang Inspektur yang tidak ditetapkan, gagal GetComponent atau Find, atau melalui varian varian "MissingReferenceException" ketika Anda memiliki referensi yang valid tetapi mendapat Destroy () ed. Jadi, saya pikir jawaban untuk pertanyaan ini dalam konteks Unity memiliki potensi bagus untuk berguna bagi masyarakat, bahkan jika Pengecualian itu sendiri sangat umum.
DMGregory

Jawaban:

14

Jenis nilai vs jenis referensi

Dalam banyak bahasa pemrograman, variabel memiliki apa yang disebut "tipe data". Dua tipe data primer adalah tipe nilai (int, float, bool, char, struct, ...) dan tipe referensi (instance dari kelas). Sementara tipe nilai mengandung nilai itu sendiri , referensi berisi alamat memori yang menunjuk ke bagian memori yang dialokasikan untuk berisi satu set nilai (mirip dengan C / C ++).

Misalnya, Vector3adalah tipe nilai (struct yang berisi koordinat dan beberapa fungsi) sementara komponen yang dilampirkan ke GameObject Anda (termasuk skrip kustom Anda yang diwarisi dari MonoBehaviour) adalah tipe referensi.

Kapan saya dapat memiliki NullReferenceException?

NullReferenceException dilemparkan ketika Anda mencoba mengakses variabel referensi yang tidak mereferensikan objek apa pun, maka itu adalah nol (alamat memori menunjuk ke 0).

Beberapa tempat umum a NullReferenceExceptionakan dimunculkan:

Memanipulasi GameObject / Komponen yang belum ditentukan dalam inspektur

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

Mengambil komponen yang tidak melekat pada GameObject dan kemudian, mencoba untuk memanipulasinya:

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

Mengakses GameObject yang tidak ada:

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

Catatan: Hati-hati, GameObject.Find, GameObject.FindWithTag, GameObject.FindObjectOfTypehanya kembali gameObjects yang diaktifkan dalam hirarki ketika fungsi ini dipanggil.

Mencoba menggunakan hasil pengambil yang kembali 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.

Mengakses elemen array yang tidak diinisialisasi

private GameObject[] myObjects ; // Uninitialized array

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

Lebih jarang, tetapi menjengkelkan jika Anda tidak tahu tentang delegasi 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) ;
}

Bagaimana cara memperbaiki ?

Jika Anda telah memahami paragraf sebelumnya, Anda tahu cara memperbaiki kesalahan: pastikan variabel Anda merujuk (menunjuk ke) instance kelas (atau mengandung setidaknya satu fungsi untuk delegasi).

Lebih mudah diucapkan daripada dilakukan? Ya memang. Berikut adalah beberapa tips untuk menghindari dan mengidentifikasi masalah.

Cara "kotor": Metode coba & tangkap:

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

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

Cara "bersih" (IMHO): Cek

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

Saat menghadapi kesalahan yang tidak bisa Anda selesaikan, selalu merupakan ide bagus untuk menemukan penyebab masalahnya. Jika Anda "malas" (atau jika masalahnya dapat dipecahkan dengan mudah), gunakan Debug.Loguntuk memperlihatkan informasi konsol yang akan membantu Anda mengidentifikasi apa yang dapat menyebabkan masalah. Cara yang lebih kompleks adalah dengan menggunakan Breakpoints dan Debugger dari IDE Anda.

Penggunaannya Debug.Logcukup berguna untuk menentukan fungsi mana yang disebut pertama misalnya. Terutama jika Anda memiliki fungsi yang bertanggung jawab untuk menginisialisasi bidang. Tapi jangan lupa untuk menghapusnya Debug.Logagar tidak mengacaukan konsol Anda (dan karena alasan kinerja).

Saran lain, jangan ragu untuk "memotong" panggilan fungsi Anda dan menambahkan Debug.Loguntuk melakukan beberapa pemeriksaan.

Dari pada :

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

Lakukan ini untuk memeriksa apakah setiap referensi diatur:

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

Debug.Log( myObject ) ;

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

Debug.Log( superComponent ) ;

superComponent.value = "foo" ;

Bahkan lebih baik :

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

Sumber:

  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
Hellium
sumber
Ini banyak upaya untuk menjelaskan "bagaimana" mendiagnosis masalah. Saya tidak akan mempertimbangkan bahwa jawaban yang sebenarnya untuk pertanyaan "apa yang masalahnya". Ini juga gagal menjawab jawaban yang biasanya muncul pada pertanyaan semacam ini. Mungkin ini akan lebih baik di dokumentasi StackOverflow? Mungkin tidak.
Gnemlock
2
Saya tidak akan mengatakan bahwa menggunakan log debug malas . Bagi saya, lebih cepat menggunakan debug.log untuk mempersempit ruang lingkup di mana kesalahan terjadi, kemudian gunakan debugger untuk benar-benar menemukan kesalahan. Tapi itu selalu tergantung pada kesalahan yang dihadapi. Bagaimanapun, saya tidak akan mengatakan menggunakan log debug malas : P
Vaillancourt
Anda juga harus menunjukkan bahwa itu tidak selalu ide yang baik untuk menempatkan cek untuk null. Ide yang lebih buruk akan digunakan try/catch. Kesalahan memberitahu Anda banyak tentang masalah yang Anda miliki di sana, dan sebelum pemula mulai meletakkan cek nol di mana-mana, masalah utama Anda ada di inspektur saat Anda lupa untuk referensi beberapa objek (seret objek ke skrip). Saya telah melihat banyak kode dengan try/catchdan membatalkan cek di tempat yang sama sekali tidak perlu. Debugging dan bekerja dengan kode seperti itu adalah "sakit di a **". Pemula belajar tentang kasus penggunaan cek tersebut dan hanya kemudian menggunakannya.
Candid Moon _Max_
Saya pikir memiliki cek nol bisa menjadi ide yang baik jika pesan Debug eksplisit disediakan di else. Memiliki NullReferenceExceptiontidak selalu jelas sendiri sementara No Rigidbody component attached to the gameObjectlangsung menjelaskan apa yang salah. Saya setuju bahwa hanya memiliki if( obj != null )pesan tanpa hanya "menyembunyikan" masalah, dan Anda dapat memiliki proyek yang berfungsi tetapi tidak melakukan apa yang Anda harapkan tanpa tahu mengapa.
Hellium
4

Meskipun kami dapat dengan mudah melakukan pemeriksaan untuk memastikan kami tidak mencoba mengakses referensi nol, ini tidak selalu merupakan solusi yang sesuai. Seringkali, dalam pemrograman Unity, masalah kita mungkin berasal dari fakta bahwa referensi tidak boleh nol. Dalam beberapa situasi, mengabaikan referensi nol dapat merusak kode kami.

Sebagai contoh, itu mungkin referensi ke pengontrol input kami. Ini sangat bagus bahwa permainan tidak crash karena pengecualian referensi null, tetapi kita perlu mencari tahu mengapa tidak ada masukan controller, dan memperbaiki yang masalah. Tanpanya, kami memiliki game yang mungkin tidak macet, tetapi tidak dapat mengambil input.

Di bawah ini, saya akan membuat daftar kemungkinan alasan dan solusi, ketika saya menemukan mereka dalam pertanyaan lain.


Apakah Anda mencoba mengakses kelas "manajer"?

Jika Anda mencoba mengakses kelas yang bertindak sebagai "manajer" (yaitu, kelas yang seharusnya hanya memiliki satu instance berjalan pada satu waktu), Anda mungkin lebih baik menggunakan pendekatan Singleton . Kelas Singleton idealnya dapat diakses dari mana saja, langsung, dengan menyimpan public staticreferensi untuk dirinya sendiri. Dengan cara ini, seorang Singleton dapat berisi referensi ke instance aktif, yang akan dapat diakses tanpa kesulitan mengatur referensi aktual setiap saat.

Apakah Anda merujuk contoh objek Anda?

Adalah umum untuk hanya menandai referensi public, sehingga kita dapat mengatur referensi ke instance melalui inspektur. Selalu periksa apakah Anda telah menetapkan referensi ke instance, melalui inspektur, karena tidak jarang ketinggalan langkah ini.

Apakah Anda membuat instance Anda?

Jika kita mengatur objek kita dalam kode, penting untuk memastikan bahwa kita membuat instance objek. Ini dapat dilakukan dengan menggunakan newkata kunci dan metode konstruktor. Sebagai contoh, pertimbangkan hal berikut:

private GameObject gameObject;

Kami telah membuat referensi ke a GameObject, tetapi tidak menunjuk ke apa pun. Mengakses referensi ini sebagaimana adanya akan menghasilkan pengecualian referensi nol . Sebelum kami mereferensikan GameObjectinstance kami , kami dapat memanggil metode konstruktor default sebagai berikut:

gameObject = new GameObject();

Tutorial Unity tentang kelas menjelaskan praktik membuat dan menggunakan konstruktor.

Apakah Anda menggunakan GetComponent<t>()metode ini dengan asumsi komponen itu ada?

Pertama, pastikan bahwa kami selalu memanggil GetComponent<t>()sebelum memanggil metode dari instance komponen.

Karena alasan yang tidak layak untuk masuk, kami dapat menganggap objek game lokal kami mengandung komponen tertentu, dan mencoba mengaksesnya GetComponent<t>(). Jika objek game lokal tidak mengandung komponen tertentu, kami akan mengembalikan nullnilai.

Anda dapat dengan mudah memeriksa apakah nilai yang dikembalikan adalah null, sebelum mengaksesnya. Namun, jika objek game Anda harus memiliki komponen yang diperlukan, mungkin lebih baik untuk memastikan bahwa itu setidaknya memiliki versi standar dari komponen itu. Kami dapat memberi tag MonoBehaviourpada [RequireComponent(typeof(t))]untuk memastikan bahwa kami selalu memiliki jenis komponen itu.

Berikut adalah contoh dari MonoBehaviouruntuk objek game yang harus selalu berisi a Rigidbody. Jika skrip ditambahkan ke objek game yang tidak mengandung a Rigidbody, default Rigidbodyakan dibuat.

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


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

Sudahkah Anda mencoba membangun kembali proyek Anda?

Ada beberapa kasus di mana Unity dapat menyebabkan masalah dengan mencoba merujuk versi cache dari objek game. Sejalan dengan solusi "matikan dan nyalakan lagi" yang lama, coba hapus folder Library Anda , dan buka kembali Unity. Persatuan akan dipaksa untuk membangun kembali proyek Anda. Ini dapat memecahkan beberapa contoh masalah yang sangat aneh ini, dan harus menunjuk ke masalah yang tidak akan muncul pada tahap akhir.

Gnemlock
sumber
1
Saya masih tidak yakin apakah pertanyaan ini harus pada topik. Tetapi di sini ada wiki komunitas bagi pengguna untuk mengirim jawaban potensial tambahan; Sejauh ini terdiri dari dasar-dasar pertama setengah halaman dari jawaban yang diterima untuk pertanyaan ditandai persatuan dan "nol referensi" (yang benar-benar memenuhi kriteria dari pertanyaan).
Gnemlock
-5

Saya melihat bahwa ada jawaban yang diterima. Tapi, ada jawaban atau saran yang lebih baik bagi Anda untuk menangani NullReferenceException. Jika Anda dapat menghubungkan pemrograman dalam bahasa Java seperti saya, Anda dapat mencegah mengirim kesalahan nol dengan menggunakan try-catchblokir. Cobalah sendiri! ;-)

Jika Anda menggunakan dalam C #, periksa apakah Anda memiliki using System;di bagian atas file skrip Anda. Jika tidak, tambahkan. Sekarang, Anda dapat menggunakan semua jenis Exceptionkelas sambil mencoba menangkap satu baris kode.

Jika Anda menggunakan UnityScript, gunakan import System;

Ini sebuah contoh:

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

        }

    }
}

Juga ingat, Anda dapat menangkap juga pengecualian lain seperti MissingReferenceException, MissingComponentException, IndexOutOfRangeException, atau kelas eksepsi lain selama Anda termasuk using Systemdalam naskah Anda.

Itu semuanya.

David Dimalanta
sumber
2
Metode coba & tangkap telah dijelaskan dalam jawaban yang diterima ....
Hellium