【まとめ】【解決後の追記】
解決まで非常に遠回りをしてしまいましたので、簡潔にまとめさせていただきます。
今回NullReferenceExceptionが出たのですが、エラーとして示された行に存在するメソッド・クラス型変数(?)に焦点をあててしまったことから見逃してしまった凡ミスです。
空参照エラーの行に存在するスクリプトやメソッドとは少し別の箇所で定義したものが原因でした。
というのも、あるゲームオブジェクトにアタッチしたスクリプトから別のゲームオブジェクトのスクリプトのメソッドを呼び出していたのですが、この時に別のオブジェクトの参照を得るべき箇所で無理やりスクリプトそのものをねじ込もうとしていました。
スクリプト冒頭で[SerializeField] private GameObject gmObj;
と書きStart()
内でスクリプトコンポーネントの参照を得るべきところで、[SerialzeField] private Script-Name scObj;
と書いてしまっていました。インスペクター側の項目もわけのわからないものを参照していました。
初心者あるあるとでも思わないとやってられない凡ミスです。基礎といいますか、扱うデータ型を疎かにしすぎです。Java勉強中とは口が裂けても言えません。
前提・実現したいこと
特別なアイテム、通常のアイテム(それぞれGameObject)を確立で生成するプログラムを作成しています。
今回、特別なアイテムを参照するtargetItem.GetGameObject()
で空参照エラーが頻発しました。
発生している問題・エラーメッセージ
特定の箇所でのみInstantiate()を実行しようとすると"NullReferenceException: Object reference not set to an instance of an object"が表示されます。
NullReferenceException: Object reference not set to an instance of an object ItemManager.makeItems (Int32 n, Vector3 vec3) (at Assets/Scripts/ItemManager.cs:71) Box.Update () (at Assets/Scripts/Box.cs:55)
該当のソースコード
C#
1//Assets/Scripts/ItemManager.cs 2public void makeItems(int n,Vector3 vec3) 3 { 4 //Debug.Log("メソッド内デバッグ : " + targetItem.GetGameObject().name); // コメント外すとエラー 5 if (canMakeTargetItem()) 6 { 7 if (true)//(Random.Range(0, 100) < targetIncidence) // 【Line68】 8 { 9 Debug.Log("チェックポインツB"); 10 Instantiate(targetItem.GetGameObject(), vec3, Quaternion.identity); //【Line71】 11 return; 12 // 特別アイテムを生成したら, 即時終了. 13 } 14 } 15 16 if (!canMakeItems()) return; 17 // 通常アイテム数が上限なら, 終了. 18 19 // 通常アイテムをn個生成【この処理は上手く働く】 20 if (n < 1) n = 1; 21 for (int i = 0; i < n; i++) 22 { 23 int shift = (i * 20) - (n*10) ; 24 Instantiate(randomItem().GetGameObject(), vec3 + new Vector3(shift,0,0), Quaternion.identity); 25 } 26 }
C#
1//Assets/Scripts/Box.cs 2void Update () { 3 4 Objects = GameObject.FindGameObjectsWithTag("StageObject"); 5 //Debug.Log("オブジェクトの数は" + Objects.Length); 6 7 int StageSt = Stage.StageState; 8 //Debug.Log(StageSt); 9 10 if(StageSt == 1){ 11 BoxHP = 0; 12 } 13 14 if (BoxHP <= 0) 15 { 16 //Debug.Log("破壊"); 17 adse.Play(); 18 itemMng.makeItems(itemMakeNum, transform.position); //【Line55】 19 Destroy(gameObject); 20 } 21 22 }
試したこと
「NullReferenceException」はよくあるnull参照エラーですから、ItemManagerクラスのtargetItem.GetGameObject()が何も示せていない事を疑いました。
同クラスのUpdate()にDebug.Log("デバッグ : " + targetItem.GetGameObject().name);
を記入したところ、オブジェクトの名前が返ってきました。しかし、同クラスのmakeItems()に上記のデバッグ文を挿入したところ、表題のようなエラーが発生しました。
makeItems()メソッド内で空参照エラーが発生する理由が思い当たりません。
デバッグ用に比較文を(true)にしてある【Line68】を本来の式に戻し、「確立で特別なアイテムを生成する」という処理にした上で問題のブロックが実行されなかった場合……「通常アイテムをn個生成」とコメントされた箇所の処理、Instantiate()は正常に動作しました。
また、Box.cs内で宝箱破壊時に効果音を再生adse.Play();
しているのですが、今回のエラー発生時、Destroy(gameObject);
されずに残っているのかSEが無限再生されるように暴走します。
特別なアイテムを生成しない、通常アイテムを生成する時はSEやDestroyに異常は感じられませんでした。
デバッグ状況1(追記)
targetItem.GetGameObject()
の補足をしますと、targetItemはItem型の変数でアイテムの各種情報(名前や属性,説明,GameObjectなど)をprivate typeで格納しています。この中にGameObjectがあり、GetGameObject()
で取得できるようになっています。
ここで、Item型のGetGameObjectは他の場所でも頻繁に利用していて問題が起きていないために見落としていましたが、問題の箇所でtargetItem
のみで確認してみたところヌルリファレンスエラーは発生しませんでした。
問題のメソッド内で<Item>.GetGameObject()を須いたときのみエラーが発生するらしいことまでが分かりました。
しかし、「targetItemの中身はどの時点でも空ではない」「GetGameObject()はprivateな変数を返すだけのメソッド」「Item型の実体の方も問題のメソッド以外では参照が確認できる」「Item型の実体の方でGameObjectの参照を格納する変数に変更を加える(代入する)処理は一切書き込んでいない」
となると、余計に分かりません。
何かちょっとしたヒントでもいいので、突破口に成るような何かが見つかるといいのですが......
デバッグ状況2(追記)
Item型のインスタンスを、ItemManagerクラスのtargetItemに参照を格納し、壊れるとアイテムを出現させるBoxクラスからItemManagerクラスのmakeItems()を実行するとエラーが発生することがわかりました。
ItemクラスのGetGameObject()だけでなく、Itemクラス内の全メソッドにおいて同様のエラーが発生します。
その条件は恐らくですが、「BoxクラスからItemManagerクラスのメソッドを呼び出す。そのメソッドがまたItemクラスのメソッドを呼び出す」といった構造をしていることだと思いましたが、それをどのように解決させるか検討もつきません。
「BoxクラスからItemManagerクラスのメソッドを呼び出す。そのメソッドがItemクラスのインスタンスの参照そのものを表示する」という先よりも一段階少ない工程でデバッグしてみると、ヌルにはなりませんでした。
HSP言語のループのように、C#のメソッドを呼び出す深さ(ネスト)が3つくらいと言った、Unity C#特有の制限があるのでしょうか。
補足情報(FW/ツールのバージョンなど)
Unity2018 2.0f2、VS2017、Windows 10 home 64bit。
回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。