UnityのNullReferenceException


11

多くのユーザーがNullReferenceException: Object reference not set to an instance of an objectUnityでエラーに直面しているため、複数のソースからこのエラーを修正するためのいくつかの説明と方法を収集することをお勧めします。


症状

コンソールに次のエラーが表示されます。それはどういう意味で、どうすれば修正できますか?

NullReferenceException:オブジェクト参照がオブジェクトのインスタンスに設定されていません


これは一般的なプログラミングの質問のようで、ゲーム開発者固有ではありません。自身の質問に対するOPの回答には、このトピックをカバーするSOへのリンクが含まれています。
ピカレク2017

3
「NullReferenceException」は確かに一般的なプログラミングの質問ですが、ここでは特にUnityの例外取り上げます。Unityプログラミングで発生する可能性のある場所とその解決方法(さまざまな例を参照)。
Hellium

@Pikalek、私たちはまた、一般的なプログラミングの観点から許可する範囲を拡大しました。これについてメタで聞いたところ、これは明らかになりました。ジョシュの回答によると、これはまだ「一般的すぎる」のパラメータに適合しているかもしれないことに気づきました。
Gnemlock 2017

現在の回答では、Unityに固有の事項についても言及していません(例ではUnity固有のタイプを使用しています)。実際、これは一般的なプログラミング応答です。私たちは近い議論で答えを使用しませんが、それが自己答えであることを考えると、それは意図の議論を支持するために行きます。
Gnemlock 2017

3
Unityには、未割り当てのInspectorフィールド、失敗したGetComponentまたはFindの試行、または有効な参照があったがDestroy()edが返された場合のバリアントフレーバー "MissingReferenceException"を介して、これらのエラーをトリガーするいくつかの固有/特徴的な方法があります。したがって、例外自体が非常に一般的であっても、Unityのコンテキストでのこの質問への回答は、コミュニティに役立つ可能性が高いと思います。
DMGregory

回答:


14

値タイプと参照タイプ

多くのプログラミング言語では、変数にはいわゆる「データ型」があります。2つの主要なデータ型は、値の型(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 ;
}

存在しない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.FindGameObject.FindWithTagGameObject.FindObjectOfTypeのみされているゲームオブジェクトを返す有効な関数が呼び出されたときの階層では。

返ってくるゲッターの結果を使おうとすると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) ;
}

直し方 ?

前の段落を理解していれば、エラーを修正する方法がわかります。変数がクラスのインスタンスを参照(または参照)している(またはデリゲート用の関数が少なくとも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_

で明示的なデバッグメッセージが提供されてelseいる場合は、nullチェックがあることをお勧めします。があることNullReferenceExceptionは必ずしも自明ではなく、No Rigidbody component attached to the gameObject何が悪いのかを直接説明します。if( obj != null )メッセージを表示しないことは問題を「隠す」だけであり、プロジェクトを機能させることはできますが、理由を知らなくても期待どおりのことはできないことに同意します。
ヘリウム

4

null参照にアクセスしないことを確認するためのチェックを簡単に行うことができますが、これは常に適切な解決策とは限りません。多くの場合、Unityプログラミングでは、問題は参照がnullであってはならないという事実に由来することがあります。状況によっては、null参照を無視するだけでコードが壊れる可能性があります。

たとえば、入力コントローラーへの参照である場合があります。null参照の例外のためにゲームがクラッシュしないのは素晴らしいことですが、入力コントローラーがない理由を理解しその問題を修正する必要があります。それがなければ、クラッシュしないかもしれませんが、入力を受け取ることもできないゲームがあります。

以下に、他の質問でそれらに遭遇したときに考えられる理由と解決策をリストします。


「マネージャー」クラスにアクセスしようとしていますか?

「マネージャー」として機能するクラス(つまり、一度に1つのインスタンスしか実行できないクラス)にアクセスしようとする場合は、シングルトンアプローチを使用する方がよいでしょう。シングルトンクラスはpublic static、自分自身への参照を保持することにより、どこからでも、直接、理想的にアクセスできます。このようにして、シングルトンにはアクティブなインスタンスへの参照を含めることができます。これにより、毎回実際の参照を設定する手間をかけることなくアクセスできます。

オブジェクトのインスタンスを参照していますか?

参照を単にとマークするのが一般的publicであるため、インスペクターを介してインスタンスへの参照を設定できます。インスペクターを使用して、インスタンスへの参照設定されていること常に確認してください。この手順を見逃すことは珍しいことではありません。

インスタンスをインスタンス化していますか?

コードでオブジェクトを設定する場合は、オブジェクトを確実にインスタンス化することが重要です。これは、newキーワードとコンストラクターメソッドを使用して実行できます。たとえば、次のことを考慮してください。

private GameObject gameObject;

への参照を作成しましたがGameObject、何も指していません。この参照にそのままアクセスすると、null参照例外が発生します。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
この質問がトピックに関するものかどうかはまだわかりません。しかし、これはユーザーが追加の潜在的な回答を投稿するためのコミュニティwikiです。これまでのところ、それは統一「null参照」(実際には質問の基準を満たした)とマークされた質問の受け入れ済み回答の前半ページの基本で構成されています
Gnemlock 2017

-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

        }

    }
}

また覚えて、あなたには、また、他の例外をキャッチすることができMissingReferenceExceptionMissingComponentExceptionIndexOutOfRangeException限り、あなたは含めるよう、またはその他の例外クラスをusing Systemスクリプトで。

以上です。


2
試し&キャッチ方法は....受け入れ答えに記載されている
Hellium
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.