Unity'de NullReferenceException


11

Birçok kullanıcı NullReferenceException: Object reference not set to an instance of an objectUnity'de hatayla karşılaştığı için , birden fazla kaynaktan bazı açıklama ve bu hatayı düzeltmenin yolları toplamanın iyi bir fikir olacağını düşündüm.


belirtiler

Aşağıdaki hatayı konsolumda görüyorum, bu ne anlama geliyor ve nasıl düzeltebilirim?

NullReferenceException: Nesne başvurusu bir nesnenin örneğine ayarlanmadı


Bu, oyun geliştirmeye özgü değil, genel bir programlama sorusu gibi görünüyor. OP'nin kendi sorusuna verdiği yanıt, bu konuyu kapsayan SO'ya bir bağlantı içerir.
Pikalek

3
"NullReferenceException" iken, gerçekten, genel programlama soru, burada, soru kapakları özellikle istisna Unity : Bu karşılaşılabilir Birlik programlama ve bunları çözmek için nasıl (çeşitli örneklere bakın).
Hellium

@Pikalek, genel programlama anlamında izin verdiğimiz kapsamı da genişlettik. Meta olarak sorduğumda bu açıklığa kavuştu . Josh'un cevabına göre, bunun hala 'çok genel' parametrelerine uygun olabileceğini anlıyorum.
Gnemlock

Şimdiki cevap , Birliğe özgü hiçbir şeyi not etmemektedir (örneklerde Birliğe özgü türler kullanmak dışında). Aslında, genel bir programlama yanıtıdır. Cevapları yakın argümanlarda kullanmıyoruz, ancak bunun kendi kendine bir cevap olduğu düşünüldüğünde, niyet argümanını desteklemeye yöneliktir.
Gnemlock

3
Unity, atanmamış Inspector alanları, başarısız GetComponent veya Find girişimleri veya geçerli bir referansınız olduğunda varyant lezzeti "MissingReferenceException" aracılığıyla bu hataları tetiklemenin birkaç benzersiz / karakteristik yoluna sahiptir, ancak Destroy () ed. Bu yüzden, Birlik bağlamında bu soruya verilen cevapların, İstisna'nın kendisi çok genel olsa bile, topluma faydalı olma potansiyeli olduğunu düşünüyorum.
DMGregory

Yanıtlar:


14

Değer türü ve Referans türü

Birçok programlama dilinde, değişkenler "veri tipi" olarak adlandırılır. İki birincil veri türü değer türleridir (int, float, bool, char, struct, ...) ve başvuru tipidir (sınıf örneği). Değer türleri değerin kendisini içermekle birlikte , başvurular belleğin bir değer kümesi (C / C ++ 'a benzer) içerecek şekilde ayrılmış bir bölümünü gösteren bir bellek adresi içerir .

Örneğin Vector3, bir değer türüdür (koordinatları ve bazı işlevleri içeren bir yapı MonoBehaviour).

Ne zaman bir NullReferenceException olabilir?

NullReferenceException herhangi bir nesneye başvurmayan bir referans değişkenine erişmeye çalıştığınızda atılır, bu nedenle null olur (bellek adresi 0'ı gösterir).

Bazı ortak yerler NullReferenceExceptionyükseltilecek:

Müfettişte belirtilmemiş bir GameObject / Bileşeni değiştirme

// 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'e bağlı olmayan bir bileşeni almak ve sonra onu değiştirmeye çalışmak:

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

Mevcut olmayan bir GameObject öğesine erişme:

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

Not: Be dikkatli, GameObject.Find, GameObject.FindWithTag, GameObject.FindObjectOfTypeokunur gameObjects dönmek etkin işlev çağrıldığında hiyerarşisinde.

Geri dönen bir alıcı sonucunu kullanmaya çalışmak 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.

Başlatılmamış bir dizinin elemanına erişme

private GameObject[] myObjects ; // Uninitialized array

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

Daha az yaygın, ancak C # delegeleri hakkında bilmiyorsanız sinir bozucu:

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

Nasıl düzeltilir ?

Önceki paragrafları anladıysanız, hatayı nasıl düzeltebileceğinizi biliyorsunuzdur: değişkeninizin bir sınıf örneğine (veya temsilciler için en az bir işlev içerdiğine) işaret ettiğinden emin olun.

Söylemesi yapmaktan kolay? Evet kesinlikle. Sorunu önlemek ve tanımlamak için bazı ipuçları .

"Kirli" yol: try & catch yöntemi:

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

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

"Daha temiz" yol (IMHO): Çek

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

Çözemediğiniz bir hatayla karşılaştığınızda , sorunun nedenini bulmak her zaman iyi bir fikirdir. "Tembel" iseniz (veya sorun kolayca çözülebiliyorsa), Debug.Logkonsol bilgisinde soruna neyin neden olabileceğini belirlemenize yardımcı olacak gösterimi kullanın . Daha karmaşık bir yol, IDE'nizin Kesme Noktalarını ve Hata Ayıklayıcısını kullanmaktır.

Debug.LogÖrneğin, ilk olarak hangi işlevin çağrıldığını belirlemek için kullanmak oldukça yararlıdır. Özellikle alanları başlatmaktan sorumlu bir fonksiyonunuz varsa. Ancak Debug.Logkonsolunuzu karmaşıklaştırmaktan (ve performans nedenlerinden dolayı) bunları kaldırmayı unutmayın .

Başka bir tavsiye, fonksiyon çağrılarını "kesmek" ve Debug.Logbazı kontroller yapmak için tereddüt etmeyin .

Onun yerine :

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

Her referansın ayarlanıp ayarlanmadığını kontrol etmek için bunu yapın:

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

Debug.Log( myObject ) ;

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

Debug.Log( superComponent ) ;

superComponent.value = "foo" ;

Daha iyi :

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

Kaynaklar:

  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

Bu, sorunu teşhis etmenin "nasıl" yapılacağını açıklamak için çok çaba sarf etmektedir. O "what sorusuna gerçek bir cevap düşünün olmaz ise sorun". Bu da genellikle bu tür sorularda ortaya çıkan cevapları ele almaz. Belki bu StackOverflow belgelerinde daha iyi olurdu? Belki de değil.
Gnemlock

2
Hata ayıklama günlüğünü kullanmanın tembel olduğunu söyleyemem . Benim için, hatanın oluştuğu yeri daraltmak için debug.log dosyasını kullanmak çok daha hızlıdır , ardından hatayı gerçekten bulmak için hata ayıklayıcıyı kullanın. Ancak her zaman eldeki hataya bağlıdır. Her halükarda, hata ayıklama günlüğünü kullanarak tembel olduğunu söyleyemem : P
Vaillancourt

Ayrıca, null için kontrol yapmanın her zaman iyi bir fikir olmadığını belirtmeliydiniz. Daha da kötüsü fikir kullanmak olurdu try/catch. Hata, orada yaşadığınız sorun hakkında çok şey anlatıyor ve yeni başlayanlar her yere boş kontroller koymadan önce, bazı nesnelere başvurmayı unuttuğunuzda ana sorun denetçide (nesneyi komut dosyasına sürükleyin). try/catchTamamen gereksiz yerlerde kod çok ve null kontrolleri gördüm . Hata ayıklama ve bunun gibi bir kodla çalışma "a ** 'da acıdır".
Samimi Ay _Max_

Ben açık bir hata ayıklama iletinin sağlanmışsa, bir boş çek olması iyi bir fikir olabilir düşünüyorum else. Neyin yanlış olduğunu doğrudan açıklarken a'ya sahip olmak NullReferenceExceptionher zaman açıklayıcı değildir No Rigidbody component attached to the gameObject. if( obj != null )Sadece mesajsız sorunun sadece "gizlendiğini" kabul ediyorum ve çalışan bir projeniz olabilir ama nedenini bilmeden beklediğiniz şeyi yapamazsınız.
Helyum

4

Boş bir referansa erişmemeye çalıştığımızdan emin olmak için kolayca bir kontrol yapabiliriz, ancak bu her zaman uygun bir çözüm değildir. Çoğu zaman, Unity programlamada, sorunumuz referansın boş olmaması gerektiğinden kaynaklanabilir. Bazı durumlarda, null referansları görmezden gelmek kodumuzu bozabilir.

Örneğin, giriş denetleyicimize bir referans olabilir. Null referans istisnası nedeniyle oyunun çökmemesi harika, ancak neden giriş denetleyicisi olmadığını bulmamız ve bu sorunu düzeltmemiz gerekiyor . Onsuz, çökmeyebilecek, ancak girdi alamayan bir oyunumuz var.

Aşağıda, diğer sorularda karşılaştığım olası nedenleri ve çözümleri listeleyeceğim.


Bir "yönetici" sınıfına erişmeye mi çalışıyorsunuz?

"Yönetici" olarak işlev gören bir sınıfa (yani, aynı anda yalnızca bir örneği çalışacak bir sınıfa) erişmeye çalışıyorsanız , Singleton yaklaşımını kullanmak daha iyi olabilir . Bir Singleton sınıfına ideal olarak her yerden, doğrudan bir public staticreferans tutarak erişilebilir . Bu şekilde, bir Singleton, her seferinde gerçek referansı ayarlama zahmetine girmeden erişilebilecek etkin örneğe bir başvuru içerebilir.

Nesnenizin örneğine mi başvuruyorsunuz?

Bir referansı basitçe olarak işaretlemek yaygındır public, böylece örneğe referansı denetçi aracılığıyla ayarlayabiliriz. Her zaman kontrol var adım kaçırmak nadir değildir gibi müfettiş aracılığıyla, bir örneğine başvuru oluşturun.

Örneğin örneğini mi oluşturuyorsun?

Nesnemizi kod halinde kurarsak , nesneyi somutlaştırdığımızdan emin olmak önemlidir . Bu, newanahtar kelime ve yapıcı yöntemleri kullanılarak gerçekleştirilebilir. Örneğin, aşağıdakileri göz önünde bulundurun:

private GameObject gameObject;

A'ya bir referans oluşturduk GameObject, ancak hiçbir şeye işaret etmiyor. Bu referansa erişme Olduğu Gibi bir neden olacaktır null başvuru özel . Örneğimize başvurmadan önce GameObject, aşağıdaki gibi varsayılan bir yapıcı yöntemi çağırabiliriz:

gameObject = new GameObject();

Sınıflar üzerine Birlik eğitimi, kurucu oluşturma ve kullanma pratiğini açıklar.

GetComponent<t>()Yöntemi, bileşenin var olduğu varsayımıyla mı kullanıyorsunuz ?

İlk olarak, her zaman aradığımızdan emin olun GetComponent<t>() bileşen örneğinden yöntemler önce .

İçeri girmeye değmez nedenlerden dolayı, yerel oyun nesnemizin belirli bir bileşen içerdiğini varsayabilir ve ona erişmeye çalışabiliriz GetComponent<t>(). Yerel oyun nesnesi değil , belirli bir parça içerirler, bir döner nulldeğer.

Dönmeden nullönce geri dönen değerin olup olmadığını kolayca kontrol edebilirsiniz . Ancak, oyun nesnenizin gerekli bileşeni olması gerekiyorsa, en azından söz konusu bileşenin varsayılan sürümüne sahip olduğundan emin olmak daha iyi olabilir . Bir etiketleyebilirizMonoBehaviour olarak[RequireComponent(typeof(t))] her zaman bileşenin bu tür sağlamak için.

İşte MonoBehaviourher zaman bir içermesi gereken bir oyun nesnesi için bir örnek Rigidbody. Komut olmayan bir oyun nesnesine eklenirse değil bir içerirler Rigidbody, varsayılan Rigidbodyoluşturulacaktır.

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


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

Projenizi yeniden inşa etmeyi denediniz mi?

Unity'nin bir oyun nesnesinin önbelleğe alınmış bir sürümüne başvurmaya çalışarak sorunlara neden olabileceği bazı durumlar vardır . Eski "kapatıp tekrar aç" çözümüne uygun olarak, Kütüphane klasörünüzü silmeyi deneyin ve Unity'yi yeniden açın. Birlik projenizi yeniden inşa etmek zorunda kalacak. Bu, bu sorunun bazı çok tuhaf örneklerini çözebilir ve son bir yapıda ortaya çıkmayacak sorunlara işaret etmelidir.


1
Bu sorunun konuyla ilgili olup olmayacağından hala emin değilim. Ancak, kullanıcıların ek potansiyel cevaplar göndermesi için bir topluluk wiki'si; Şimdiye kadar, birlik ve "boş referans" olarak işaretlenen sorulara (aslında sorunun ölçütlerini karşılayan) kabul edilen cevapların ilk yarım sayfasının temellerinden oluşmaktadır .
Gnemlock

-5

Görüyorum ki kabul edilmiş bir cevap var. Ancak, ele almanız için daha iyi bir cevap veya öneri var NullReferenceException. Programlamayı benim gibi Java dilinde ilişkilendirebiliyorsanız, bu programı kullanarak boş bir hata göndermeyi önleyebilirsiniz.try-catch bloğu . Kendiniz deneyin! ;-)

C # 'da kullanıyorsanız using System;komut dosyanızın üstünde olup olmadığını kontrol edin . Değilse ekleyin. Artık her türlüException bir kod satırını yakalamaya çalışırken sınıfı .

UnityScript kullanıyorsanız, import System;

İşte bir örnek:

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

        }

    }
}

Ayrıca aşağıdakiler gibi diğer özel durumları yakalamak, hatırlamak MissingReferenceException, MissingComponentException, IndexOutOfRangeExceptionsürece dahil olarak ya da herhangi bir diğer istisna sınıflarıusing System Betiğinizde.

Hepsi bu.


2
Try & catch yöntemi kabul edilen cevapta açıklanmıştır ....
Hellium
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.