Unity의 NullReferenceException


11

많은 사용자가 NullReferenceException: Object reference not set to an instance of an objectUnity 에서 오류에 직면하고 있기 때문에 여러 소스 에서이 오류를 해결하는 방법과 설명을 수집하는 것이 좋습니다.


조짐

콘솔에 아래 오류가 표시됩니다. 무슨 의미이며 어떻게 수정합니까?

NullReferenceException : 객체 참조가 객체의 인스턴스로 설정되지 않았습니다


이것은 일반적인 프로그래밍 질문처럼 보이며 게임 개발자에게는 해당되지 않습니다. 자신의 질문에 대한 OP의 답변에는이 주제를 다루는 SO에 대한 링크가 포함되어 있습니다.
Pikalek

3
"NullReferenceException"은 실제로 일반적인 프로그래밍 질문이지만,이 질문은 Unity 의 예외 구체적으로 다룹니다 . Unity 프로그래밍 에서 어디에서 발생할 수 있는지, 그리고이를 해결하는 방법 (다양한 예 참조).
Hellium

@Pikalek은 일반 프로그래밍 측면에서 허용 범위를 넓혔습니다. 내가 메타로 그것에 대해 물었을 때 이것은 명확 해졌다 . 나는 조쉬의 대답에 따라 이것이 여전히 '너무 일반적인'매개 변수에 적합 할 수 있음을 알고 있습니다.
Gnemlock

현재의 대답은 아무것도 전혀 참고하지 않습니다 특정 (예에서 유니티의 특정 유형을 사용하는 것보다 다른) 유니티로합니다. 실제로는 일반적인 프로그래밍 응답입니다. 우리는 긴밀한 주장에 답을 사용하지는 않지만, 자기 답이 주어지면 의도의 주장을지지하는쪽으로 나아가게됩니다.
Gnemlock

3
Unity에는 할당되지 않은 Inspector 필드, 실패한 GetComponent 또는 Find 시도 또는 유효한 참조가 있지만 Destroy ()가있는 변형 변형 "MissingReferenceException"을 통해 이러한 오류를 트리거하는 몇 가지 고유하고 특징적인 방법이 있습니다. 따라서 Unity의 맥락 에서이 질문에 대한 답변은 예외 자체가 매우 일반적인 경우에도 커뮤니티에 유용 할 수있는 좋은 잠재력이 있다고 생각합니다.
DMGregory

답변:


14

값 유형과 참조 유형

많은 프로그래밍 언어에서 변수에는 "데이터 유형"이 있습니다. 두 가지 주요 데이터 유형은 값 유형 (int, float, bool, char, struct, ...) 및 참조 유형 (클래스 인스턴스)입니다. 값 유형에는 값 자체 가 포함되지만 , 참조에는 값 세트를 포함하도록 할당 된 메모리 부분을 가리키는 메모리 주소 가 포함됩니다 (C / C ++와 유사).

예를 들어, Vector3값 유형 (좌표 및 일부 함수를 포함하는 구조체)이고 GameObject에 연결된 컴포넌트 (에서 상속되는 커스텀 스크립트 포함 MonoBehaviour)는 참조 유형입니다.

언제 NullReferenceException이 발생할 수 있습니까?

NullReferenceException 객체를 참조하지 않는 참조 변수에 액세스하려고하면 null이므로 메모리 주소가 0을 가리 킵니다.

몇 가지 일반적인 장소 NullReferenceException가 제기됩니다.

인스펙터에 지정되지 않은 게임 오브젝트 / 컴포넌트 조작

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

존재하지 않는 게임 오브젝트에 액세스 :

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전용 게임 오브젝트 반환 활성화 함수가 호출 될 때 계층 구조를.

반환하는 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) ;
}

"깨끗한"방법 (IMHO) : 점검

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

출처 :

  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

이것은 문제 진단의 "방법"을 설명하기 위해 많은 노력을 기울입니다. 나는 " 문제 무엇인가"라는 질문에 대한 실제 답변을 고려하지 않을 것 입니다 . 또한 이러한 종류의 질문에 일반적으로 나타나는 답변을 설명하지 못합니다. 아마도 이것이 StackOverflow 문서에서 더 좋을 것입니까? 아마 아닐거야.
Gnemlock

2
디버그 로그 사용이 게으른 것이라고는 말하지 않을 것입니다 . 나를 위해 debug.log를 사용하여 오류가 발생하는 범위를 좁힌 다음 디버거를 사용하여 실제로 오류를 찾는 것이 훨씬 빠릅니다 . 그러나 그것은 항상 당면한 오류에 달려 있습니다. 어쨌든, 디버그 로그를 사용하는 것이 게으르다 고 말하지 않을 것입니다 : P
Vaillancourt

또한 null을 검사하는 것이 항상 좋은 생각은 아니라는 점을 지적해야합니다. 더 나쁜 생각은 사용하는 것 try/catch입니다. 이 오류는 발생한 문제에 대해 많은 정보를 제공하며 초보자가 모든 곳에서 null 검사를 시작하기 전에 일부 개체를 참조하는 것을 잊어 버린 경우 주요 문제는 관리자에게 있습니다 (스크립트로 개체를 드래그). 나는 try/catch완전히 불필요한 곳에서 많은 코드와 null 검사를 보았습니다 . 이와 같은 코드를 디버깅하고 사용하는 것은 "a **의 고통"입니다. 초보자는 이러한 검사의 사용 사례에 대해 배우고 사용합니다.
솔직한 달 _Max_

에 명시적인 디버그 메시지가 제공되는 경우 null 검사를 사용하는 것이 좋습니다 else. 를 갖는 것은 NullReferenceException항상 자기가 설명하는 동안하지 않습니다 No Rigidbody component attached to the gameObject직접 문제가 있는지 설명한다. if( obj != null )메시지없이 메시지를 보내는 것만으로도 문제를 "숨길"수 있으며, 프로젝트를 진행할 수 있지만 이유를 모른 채 예상하지 못한 작업을 수행 할 수 있다는 데 동의합니다 .
Hellium

4

null 참조에 액세스하려고하지 않는지 확인하기 만하면되지만 이것이 항상 적합한 솔루션은 아닙니다. 여러 번 Unity 프로그래밍에서 우리의 문제는 참조 null 이 아니어야 한다는 사실에서 비롯 될 수 있습니다. 경우에 따라 단순히 null 참조를 무시하면 코드가 손상 될 수 있습니다.

예를 들어, 입력 컨트롤러에 대한 참조 일 수 있습니다. null 참조 예외로 인해 게임이 충돌하지 않는 것이 좋지만 입력 컨트롤러가없는 이유 를 파악 하고 문제 해결해야합니다. 그것 없이는, 우리는 충돌하지는 않지만 입력을받을 수없는 게임을 가지고 있습니다.

아래에는 가능한 다른 이유와 해결책이 나와 있습니다.


"관리자"수업에 액세스하려고하십니까?

"관리자"역할을하는 클래스 (즉, 한 번에 하나의 인스턴스 만 실행해야하는 클래스)에 액세스하려는 경우 Singleton 접근 방식을 사용 하는 것이 좋습니다 . Singleton 클래스는 public static자신에 대한 참조를 유지하여 어디에서나 직접 액세스하는 것이 이상적 입니다. 이러한 방식으로 싱글 톤은 활성 인스턴스에 대한 참조를 포함 할 수 있으며, 매번 실제 참조를 설정하는 데 어려움이없이 액세스 할 수 있습니다.

객체의 인스턴스를 참조하고 있습니까?

단순히 참조를로 표시하는 것이 일반적 public이므로 인스펙터를 통해 인스턴스에 대한 참조를 설정할 수 있습니다. 항상 당신이 확인 것이이 단계를 그리워하는 것은 드문 일이 아니다 같이 관리자를 통해 인스턴스에 대한 참조를 설정합니다.

인스턴스를 인스턴스화하고 있습니까?

코드에서 객체를 설정하는 경우 객체를 인스턴스화 해야합니다. new키워드와 생성자 메소드를 사용하여 수행 할 수 있습니다 . 예를 들어 다음을 고려하십시오.

private GameObject gameObject;

에 대한 참조를 GameObject만들었지 만 아무 것도 가리 키지 않습니다. 이 참조 액세스 바와 같이하면 (A)에 발생한다 널 참조 예외 . GameObject인스턴스 를 참조하기 전에 다음과 같이 기본 생성자 메서드를 호출 할 수 있습니다.

gameObject = new GameObject();

클래스의 Unity 튜토리얼에서는 생성자를 만들고 사용하는 방법을 설명합니다.

GetComponent<t>()구성 요소가 존재한다는 가정하에이 방법 을 사용하고 있습니까?

먼저 GetComponent<t>()컴포넌트 인스턴스에서 메소드를 호출 하기 전에 항상 호출 해야합니다.

갈 가치가없는 이유로 로컬 게임 객체에 특정 구성 요소가 포함되어 있다고 가정하고로 액세스하십시오 GetComponent<t>(). 로컬 게임 오브젝트에 해당 특정 컴포넌트가 포함되어 있지 않으면null 값 을 반환 합니다.

null액세스하기 전에 반환 값이 인 경우 쉽게 확인할 수 있습니다 . 그러나 게임 오브젝트 에 필수 컴포넌트 있어야하는 경우 최소한 해당 컴포넌트 의 기본 버전 이 있는지 확인하는 것이 좋습니다 . 항상 해당 유형의 구성 요소를 갖도록 MonoBehaviouras [RequireComponent(typeof(t))]에 태그를 지정할 수 있습니다 .

다음은 MonoBehaviour항상을 포함해야하는 게임 오브젝트 의 예입니다 Rigidbody. 이 포함 되지 않은 게임 오브젝트에 스크립트가 추가 Rigidbody되면 기본값 Rigidbody이 생성됩니다.

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


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

프로젝트를 다시 빌드하려고 했습니까?

캐시 된 버전의 게임 오브젝트 를 참조하여 Unity가 문제를 일으킬 수있는 경우가 있습니다 . 오래된 "껐다가 다시 켜십시오"솔루션에 따라 라이브러리 폴더를 삭제 하고 Unity를 다시 엽니 다. Unity는 프로젝트를 다시 빌드해야합니다. 이를 통해이 문제의 매우 독특한 사례를 해결할 수 있으며 최종 빌드에서 나타나지 않는 문제를 지적해야합니다.


1
이 질문이 주제인지에 대해서는 아직 확실하지 않습니다. 그러나 여기에 사용자가 추가 잠재적 답변을 게시 할 수있는 커뮤니티 위키가 있습니다. 지금까지이 구성되어 기본 최초의 반 페이지 표시 질문에 허용 답변의 통일성"널 (null) 참조" (즉, 실제로 문제의 기준을 충족).
Gnemlock

-5

나는 대답이 받아 들여지는 것을 본다. 그러나 처리에 대한 더 나은 답변이나 제안이 있습니다 NullReferenceException. 나와 같은 Java 언어로 프로그래밍을 관련시킬 수 있으면 try-catch블록 을 사용하여 null 오류를 보내지 못하게 할 수 있습니다 . 직접 해보십시오! ;-)

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스크립트에서.

그게 다야


2
try & catch 방법은 허용 된 답변에 설명되어 있습니다 ....
Hellium
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.