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

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

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

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

URL

URL(ユニフォームリソースロケータ)とは、インターネット上のリソース(Webページや電子メールの宛先等)を特定するための形式的な記号の並びの事を言う。

Unity

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

Q&A

解決済

1回答

1776閲覧

UnityのC#スクリプトで、別のスクリプトで書いたCoroutineを実行したい/NullReferenceExceptionがでる

george6493

総合スコア25

C#

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

URL

URL(ユニフォームリソースロケータ)とは、インターネット上のリソース(Webページや電子メールの宛先等)を特定するための形式的な記号の並びの事を言う。

Unity

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

0グッド

0クリップ

投稿2019/10/19 09:37

編集2019/10/19 14:33

#状況
シーン上には
・Button1
・Button2
・Button3
という3つのオブジェクトがある。

Button1にアタッチしたスクリプトで、String変数”Type"で条件分岐し「指定されたURLへリクエストを送って自身のテクスチャを変更する」「子要素のマテリアルの色を指定の色に変更する」という処理を行なうコルーチンを定義した。
これをUpdate()内で呼んで実行したところ、期待通り動作した。

他のButtonもButton1と同じように色やテクスチャ画像を変更したいが、条件分岐の処理はまとめてどこかに置いておきたいので(でないと分岐が一つ加わると全部変えないといけない)、他のオブジェクトではこのスクリプトに書いた同一のコルーチンを呼び出す形で使いたい。

#試したこと
とりあえず、Button2にアタッチするスクリプトでpublic Button1Script button1Scriptとして、UnityのGUI上でオブジェクトを紐付けた上で、Update()内で StartCoroutine(button1Script.myCoroutine()); という形で呼んでみたところ、

NullReferenceException: Object reference not set to an instance of an object

と言われた。

#書いたコード抜粋
Button1Script

C#

1public IEnumerator myCoroutine() 2 { 3 bool once = true; 4 string url; 5 Texture2D tex; 6 Color32 color; 7 8 if (once) 9 { 10 if (type == "twitter") 11 { 12 url = "hoge"; 13 color = new Color32(29, 161, 250, 255); 14 } 15 else if (type == "facebook") 16 { 17 url = "hogehoge"; 18 color = new Color32(36, 119, 241, 255); 19 } 20 else 21 { 22 url = ""; 23 color = new Color32(0, 0, 0, 0); 24 } 25 26 27 var request = UnityWebRequestTexture.GetTexture(url); 28 yield return request.Send(); 29 tex = new Texture2D(4, 4, TextureFormat.DXT1, false); 30 31 tex.LoadImage(request.downloadHandler.data); 32 GetComponent<Renderer>().material.mainTexture = tex; 33 cylinder.GetComponent<Renderer>().material.color = color; 34 once = false; 35 yield break; 36 } 37 } 38 39 // Update is called once per frame 40 void Update() 41 { 42 type = "hogehoge";//こちらも別スクリプトから取得してきた変数 43 44 StartCoroutine(myCoroutine()); 45 46 }

Button2Script

C#

1public Button1Script button1Script;//GUIでできたエリアにhierarchyからButton1を突っ込む 2void Update(){ 3type = "hogehogehoge"; 4StartCoroutine(button1Script.myCoroutine());//エラーメッセージ発生箇所

#教えていただきたいこと
Button2Scriptで、これに近い形でコルーチンを呼び出す方法はありますでしょうか?
もし難しそうであれば、「これならいける」という方法をご教授いただけますでしょうか。

散々調べたのですがどうも適切そうな文献が見当たらず。

まだ未熟で稚拙な質問となってしまったかもしれませんが、何卒よろしくお願いいたします!
もし追加で必要な情報等ありましたら迅速に対応させていただきますのでお申し付けくださいませ。

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

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

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

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

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

fiveHundred

2019/10/19 09:46

エラーを見る限り、button1Scriptがnull(=設定されていない)となっておりますが、本当に紐付けされているのでしょうか?
george6493

2019/10/19 10:08

先ほど追記致しました。GUIから紐づけております。むしろ、StartCoroutineでの指定の方法に問題があるのではないか?と思いまして、Button1ScriptでコルーチンをPublicでなくしてみたのですが、そうしたら「Publicではないのでダメです」というエラーが出たので、一応指定自体は通っているみたいです。なので、コルーチン自体よりもその中で何か起こっているのだろうなと素人ながら考えたのですが、いかがでしょうか?
fiveHundred

2019/10/19 10:13

エラーに発生箇所が併記されているはずなので、まずはそれを確認してみてください。
george6493

2019/10/19 11:53

発生箇所はStartCoroutineの行になってましてとくに情報がなく苦しんでおりました、、
fiveHundred

2019/10/19 12:16

発生行だけでなく、スタックトレースも確認してみてください。 また、ブレークポイントかDebug.Log()をコルーチン内の各箇所に配置して、コルーチンが呼ばれているかやその中の処理がどこまで正しく実行できているかを確認してみてください。 それでも分からない場合は、すみませんが私には分からないと思います。
YAmaGNZ

2019/10/19 13:00

button1Scriptがnullなだけじゃないの?
george6493

2019/10/19 14:40

fiveHundredさま、ありがとうございます。やはりコルーチンは呼ばれており、urlも取得しているようです。YAmaGNZさま、ありがとうございます。button1Script内では動いているコルーチンが、どうしてnullになってしまっているのでしょうか?
george6493

2019/10/19 15:03

先ほど、Button1,2共にコルーチン呼び出しの際引数に「type」を記して使ったところ、Button1上でButton2のTypeとフレームごとに入れ替わりで表示されるようになりました。何かヒントになりそうでしょうか
vo3

2019/10/19 16:14

ところで、Button1にはRendererコンポーネントが割り付いていて、cylinderの参照もあり、かつcylinderにはRendererコンポーネントが割り付いているでいいんですよね?
george6493

2019/10/19 17:33

vo3さま、間違いありません。ボタンのUI部分をQuadのテクスチャを変えることで表示して、Quadの下にCylinderを重ねることでボタンを雑に表現しています。そして、Button1にアタッチしたButton1Scriptでは正常に動作しております。
vo3

2019/10/20 00:07

で、00:03のコメントから判断すると、コルーチン引数にtypeを渡した場合は意図した動きではないけどNullExceptionはでなくなったということでよろしいですか?
guest

回答1

0

ベストアンサー

NullPointerException がどこで発生しているかはこの情報ではちょっとわかりません。
他の方々が言われているように button1Script がnullになっているが一番考えられます。
ただ、 type を引数渡しにした場合に正常動作したのであれば、ここに記載されているコード以外が影響している可能性があります。

というのも、george6493さんは恐らく 【Button2でbutton1Script.myCoroutineを実行すれば、Button2のパラメータで動作する】と考えられているのだと思いますが、myCoroutineは Button1のインスタンスメソッド なのでButton1上のパラメータで動作します。
なので、例えばcilinderはButton1のどこで取得したかによっておかしくなる可能性はあります。

type を引数に渡して正常動作したのは、

type = "hogehogehoge"; StartCoroutine(button1Script.myCoroutine());

でtypeを設定しても button1Script.myCoroutine 内のtypeには一切関係ないので分岐で必ずelseになっていたのが、ようやく判断できるようになったからです。
button1Script はあくまでも参照先の保存ですので、みんながButton1を見に行くようになるわけです。

ちなみにですが、コルーチン内で宣言した変数はそのコルーチン内でしか効果がありません。
その為、コルーチン内で

bool once = true; if (once) { yield return 数フレームの処理 once = false; }

を実行しても呼び出しの度に実行されます。
なので、Button1Script/Button2ScriptともにUpdateで実行されているのはテストだと思いたいのですが、この二つが同一プロジェクトで有効な場合は URLから取得が時間がかかるものだとしても毎フレームButton1/Button2のどちらからもリクエストが実行される ようになっています。
今現在だと myCoroutine を以下のように変更すると

Debug.Log(gameObject.name + "S"); bool once = true; if (once) { yield return new WaitForSeconds(1); // 一秒まつ once = false; Debug.Log(gameObject.name + "E"); yield break; } Debug.Log(gameObject.name + "once lock E");
Button1S //(Button1のUpdate) Button1S //(Button2のUpdate) Button1S Button1S ... Button1S Button1S Button1S Button1S Button1E // 最初のButton1からのリクエストから1秒経過 Button1E // 最初のButton2からのリクエストから1秒経過 Button1S Button1S Button1E Button1E Button1S Button1S ...

のようにButton1上でしか動作せず、「once lock E」というログも出ることなく、リクエストしまくるようになっています。
本仕様なのであればこちらの呼び出しは変えないとものすごい数のリクエストが実行されるので変更すべきです。

意図したものにしたいのであれば、少なくとも
0. once フラグは Button1Script のメンバ変数にする(この場合はプロジェクト上で一つのみ同時リクエストが可)
0. 外から使えるように必要とするもの(type/GameObject)は引数で渡す

の二つは変更が必要です。
または
0. once フラグは各自が持って抑制する(呼び出し元が責任持たないと同じようになる)
0. myCoroutine はインスタンスメソッドではなくStaticメソッドにする
0. 外から使えるように必要とするもの(type/GameObject)は引数で渡す

です。
あとは、(むしろこちらが推奨)このテクスチャリクエストの処理を Button1 から切り離した別のスクリプトにし、必要とするGameObjectにそれぞれ割り付けるがいいです。
もちろん、onceフラグはコルーチンの中ではなく外で宣言してください。

投稿2019/10/20 01:02

vo3

総合スコア321

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

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

george6493

2019/10/21 08:34

vo3様、本当にありがとうございます!!おかげさまで、 ・Unityでのデバッグの手法 ・変数のスコープ ・コルーチンって結局なんなのか といった、自分でググっていた時にモヤモヤしていた部分がかなり整理できました(実はUnity触って一ヶ月ほどでC#も都度拾ったハウツーと雰囲気で書いてました)。 結局、推奨いただきました通り、ButtonManagerという別オブジェクトにアタッチしたスクリプトでButton1-3の情報を受け取って処理した後に渡す。onceはメンバ関数にしておいて、見せていただいたデバッグ手法でボタン3つ分のリクエストがそれぞれ1回ずつ行われていることを確認することができました。 稚拙すぎて恥ずかしい質問でしたが、vo3さまのおかげで理解に繋げることができました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問