質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

3回答

1286閲覧

NullReferenceだけは許さない…

kogarashi0917

総合スコア12

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2021/06/16 08:42

Unityで2Dアクションゲームを作っていて、アイテムをステージ上のランダムな地上に生成させるスクリプトを書いたのですが、下記コードでNullReferenceが呼び出されて若干萎えてます。

collider.tagだと処理が重くなるからCompareTagに変えてやったというのに、何がいけないんだーッ!

…何を参照すれば解決するのか教えて下さると助かります。

c#

1public class Item : MonoBehaviour 2{ 3 /* public変数 */ 4 public float stageLeft, stageRight, stageHeight; 5 6 /* private変数 */ 7 [SerializeField] GameObject itemPref = null; 8 GameObject itemInstance; 9 Ray ray; 10 RaycastHit2D hit; 11 Vector2 tPos; 12 int count = 0; 13 float distance = 11.0f; 14 15 void Start() 16 { 17 while(count < 9) 18 { 19 tPos = new Vector2(Random.Range(stageLeft, stageRight), stageHeight); 20 ray = new Ray(tPos, -transform.up); 21 Debug.DrawLine(tPos, ray.direction * distance, Color.red); 22 23 if(Physics2D.Raycast(tPos, -transform.up)) 24 { 25 if (hit.collider.CompareTag("Ground")) //←ここでNullReferenceが起こる 26 { 27 itemInstance = Instantiate(itemPref) as GameObject; 28 itemInstance.name = itemPref.name; 29 itemInstance.transform.position = new Vector3(tPos.x, hit.collider.transform.position.y, -3.0f); 30 31 count++; 32 } 33 34 else 35 { 36 continue; 37 } 38 } 39 } 40 } 41 42}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/06/16 09:19

> collider.tagだと処理が重くなるからCompareTagに変えてやったというのに、何がいけないんだーッ! 変更はその1点だけでしょうか。 また、「collider.tag」を使用し場合は発生しない、ということでよろしいでしょうか。
guest

回答3

0

ベストアンサー

hitには何も代入されていません。
なので、hit.collider.CompareTag("Ground")でNullReferenceExceptionになるのは当然です。

以下のようにPhysics2D.Raycast(tPos, -transform.up)の返り値をhitに代入していないのが原因でしょう。

間違い:

C#

1 if(Physics2D.Raycast(tPos, -transform.up)) 2 { 3 // …(中略) 4 }

正しい:

C#

1 hit = Physics2D.Raycast(tPos, -transform.up); 2 if(hit) // 「if(hit != null)」でも同じ 3 { 4 // …(中略) 5 }

投稿2021/06/16 09:17

fiveHundred

総合スコア9797

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kogarashi0917

2021/06/16 09:32

ご回答ありがとうございます。 実際に修正して実行してみたところ、固まったまま処理が進まなくなってしまいます。 Start()内で処理を行うのは危険だったりしますか?
fiveHundred

2021/06/16 09:54

現状のコードだと、地面に設置していないと無限ループになります。 どこかでループを抜けない限り、次の処理が行えず、ゲーム全体がフリーズします。 このような書き方は、やってはいけません。 (コルーチンでループ内に「yield return ~;」と書き、適宜抜ける方法もありますが)
kogarashi0917

2021/06/17 07:14

日を跨いでの質問失礼します。 public class Item : MonoBehaviour { /* public変数 */ public float stageLeft, stageRight, stageHeight; /* private変数 */ [SerializeField] GameObject itemPref = null; GameObject itemInstance; Ray ray; RaycastHit2D hit; Vector2 tPos; int count = 0; float distance = 11.0f; void Start() { StartCoroutine(ItemSetByRay()); } IEnumerator ItemSetByRay() { while (count < 9) { tPos = new Vector2(Random.Range(stageLeft, stageRight), stageHeight); ray = new Ray(tPos, -transform.up); hit = Physics2D.Raycast(tPos, -transform.up); Debug.DrawLine(tPos, ray.direction * distance, Color.red); if (hit) { if (hit.collider.CompareTag("Ground")) { itemInstance = Instantiate(itemPref) as GameObject; itemInstance.name = itemPref.name; itemInstance.transform.position = new Vector3(tPos.x, hit.collider.transform.position.y, -3.0f); count++; } else { continue; } } } yield break; } } このようにコードを書き換えてみたのですが、yieldを差し込む位置としてはどこが良いのでしょうか?
fiveHundred

2021/06/17 07:45

ちょっと勘違いしていたので、コルーチンやyieldのことは一度忘れてください(すみません)。 無限ループになっているのが問題であって、1フレーム内に収まればStart()の中でも特に問題はないです。 この場合、countが9になれば抜けますが、「Physics2D.Raycast(tPos, -transform.up);の結果がnullではないこと」「hit.collider.CompareTag("Ground")がtrueであること」の両方を満たしていなければ、countが増えません。 そのため、これらがどうやっても成立していなければ、無限ループになります。 例えば「位置などの関係でどうやってもGroundに当たらない」「そもそもGroundというタグのものが存在しない」場合がこれに該当するので、そうなっていないか一度確認してください。 また、質問とは関係ないですが、関数内でしか使わない変数は、関数の中で定義したほうがよろしいかと思います。
kogarashi0917

2021/06/17 07:51

> この場合、countが9になれば抜けますが、「Physics2D.Raycast(tPos, -transform.up);の結果がnullではないこと」「hit.collider.CompareTag("Ground")がtrueであること」の両方を満たしていなければ、countが増えません。 なるほどなるほど… 先ほど上記のコードでループの原因を探ろうとしたところ、hit.colliderにCinemachineのCompositeColliderが代入されていました。 CompositeColliderにはタグ付けしていないので無限ループが発生してしまうのはわかるのですが、もしかしてRaycastとCinemachineは共存できなかったりするのでしょうか?
fiveHundred

2021/06/17 08:04

Physics2D.RaycastにlayerMaskを指定すれば、レイが当たるレイヤーを指定することができます。 それを利用して、例えば「Ground」というレイヤーを作っておき、それのみをlayerMaskで指定すればよろしいかと。 (この場合、Groundタグが不要になるかもしれません) あるいは、どうしてもレイヤーを指定したくないのであれば、Physics2D.RaycastAllで全て取得して、どれかがGroundタグであれば~、といった感じで処理することも出来ます。 (その分処理が多くなるので、前者をおすすめしますが)
hogefugapiyo

2021/06/17 08:14

まずループさせずに1回の処理で作ってみたらどうですか? それならデバッグも楽ですし。 ちゃんとGroundにアイテムが1個生成されたら、たとえばそれをループではなくて2回連続で処理してみて動くか確認。それも動くならループ文書いて最終的な目的を達成すればよいのではないかと
guest

0

それが出る場所で実行を止めて、そこに現れる変数の値を片っ端からチェックしよう
nullになってるのはないでしょうか

投稿2021/06/16 08:45

y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kogarashi0917

2021/06/16 09:16

ご回答ありがとうございます。 y_waiwaiさんのおっしゃる通り、hit.colliderがnullになっていました。汗 追加で質問して申し訳ないのですが、 このhit.colliderをnullでなくするには、あらかじめ宣言しておいたほうが良いのでしょうか?
hogefugapiyo

2021/06/16 09:21

fiveHundred さんが答えてたので詳しくはそちらですが。キャストした結果をhitにいれて、hitがnullじゃなければ処理ってすれば、Raycastがヒットしたことを担保できるのでNullになりません
guest

0

RaycastHit2D.collider

RaycastHit2D を返すいくつかの関数はコライダーを Null としたままにすることに注意してください。これは何も衝突してないことを示します。RaycastHit2D は明示的な変換の演算子を実装しているため、このプロパティーをチェックして衝突があったかどうかを boolean で返します。

上記の通り、「collider」がNullの場合があるため、使用する前に判定を行いましょう。

投稿2021/06/16 09:24

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問