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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Unity

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

Q&A

解決済

1回答

6506閲覧

Unityで別シーンのオブジェクトにアタッチされているスクリプト内のメソッドを使用したい

yryo1005

総合スコア1

Unity

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

0グッド

0クリップ

投稿2020/11/13 19:23

Unityで別シーンのオブジェクトにアタッチされているスクリプト内のメソッドを使用したい

ここに質問の内容を詳しく書いてください。
(例)PHP(CakePHP)で●●なシステムを作っています。
■■な機能を実装中に以下のエラーメッセージが発生しました。

Unityでゲームを作成しています
リザルト画面にゲームのスコアを表示したいので、そのためのメソッドを作成しておりましたが、恐らく別シーンのスクリプトの参照がうまくいっていません

詳しい状況の説明ですが
『シーン_ゲーム画面(昼)』というシーンの「GameControler_Hiru」という空のオブジェクトに、〈GameControl_Hiru〉というスクリプトをアタッチ
『シーン_ゲーム画面(昼)』というシーンの「障害物」というオブジェクトに、〈CabeBig〉というスクリプトをアタッチ
『シーン_リザルト画面』というシーンの「GameControler_Result」というオブジェクトに、〈Result〉というスクリプトをアタッチしております。

やりたいこととしては、『シーン_ゲーム画面(昼)』内で障害物に主人公が当たった場合に、〈CabeBig〉スクリプト内の≪OnCollisionStay2D≫メソッドで〈GameControl_Hiru〉スクリプト内の≪SendScore≫メソッドを呼び出し、≪SendScore≫メソッドで、別シーンの〈Result〉スクリプト内の≪get_score≫メソッドを呼び出しスコアを橋渡ししたいと思っています

発生している問題・エラーメッセージ

NullReferenceException: Object reference not set to an instance of an object GameControl_Hiru.SendScore () (at Assets/スクリプト/ゲーム画面(昼)/GameControl_Hiru.cs:127) CabeBig.OnCollisionStay2D (UnityEngine.Collision2D col) (at Assets/スクリプト/CabeBig.cs:36)

該当のソースコード

C

1CabeBigスクリプト 2 3using System.Collections; 4using System.Collections.Generic; 5using UnityEngine; 6using UnityEngine.SceneManagement; 7 8public class CabeBig : MonoBehaviour 9{ 10 private GameObject player; 11 12 // Start is called before the first frame update 13 void Start() 14 { 15 player = GameObject.Find("プレハブ_紙飛行機"); 16 } 17 18 // Update is called once per frame 19 void Update() 20 { 21 Vector3 playerPos = player.transform.position; 22 Vector3 thisPos = this.transform.position; 23 24 if (playerPos.y - thisPos.y < -4) 25 { 26 Destroy(this.gameObject); 27 } 28 29 } 30 31 void OnCollisionStay2D(Collision2D col) 32 { 33 if (col.gameObject.name == "プレハブ_紙飛行機") 34 { 35 if (SceneManager.GetActiveScene().name == "シーン_ゲーム画面(昼)") 36 { 37 GameObject game_root = GameObject.Find("GameControler_Hiru"); 38 game_root.GetComponent<GameControl_Hiru>().SendScore(); 39 } 40 else 41 { 42 GameObject game_root = GameObject.Find("GameControler"); 43 game_root.GetComponent<GameControl_Yoru>().SendScore(); 44 } 45 } 46 } 47} 48 49-------------------------------------------------------- 50GameControl_Hiruスクリプト 51 52using System; 53using System.Collections; 54using System.Collections.Generic; 55using System.Diagnostics; 56using UnityEngine; 57using UnityEngine.UI; 58using UnityEngine.SceneManagement; 59 60public class GameControl_Hiru : MonoBehaviour 61{ 62 63 public GameObject prefab_cloude; 64 public GameObject prefab_coin; 65 public GameObject prefab_cabe; 66 public GameObject prefab_cabe2; 67 68 public double OldplayerPos; 69 70 private GameObject player; 71 72 private float timer = 0.0f; 73 public int score = 0; 74 public Text textTimer; 75 76 public int Coin = 0; 77 public Text textCoin; 78 79 // Start is called before the first frame update 80 void Start() 81 { 82 player = GameObject.Find("プレハブ_紙飛行機"); 83 Vector3 playerPos = player.transform.position; 84 OldplayerPos = playerPos.y; 85 } 86 87 // Update is called once per frame 88 void Update() 89 { 90 timer += Time.deltaTime; 91 score = (int)timer; 92 textTimer.text = score.ToString(); 93 94 Vector3 playerPos = player.transform.position; 95 96 if (UnityEngine.Random.Range(0, 300) == 0) 97 { 98 CreateCloude(); 99 } 100 101 if (UnityEngine.Random.Range(0, 3000) == 0) 102 { 103 CreateCoin(); 104 105 } 106 107 if (OldplayerPos - playerPos.y >= 4) 108 { 109 CreateCabe(); 110 OldplayerPos = playerPos.y; 111 } 112 113 } 114 115 private void CreateCloude() 116 { 117 118 Vector3 playerPos = player.transform.position; 119 120 GameObject cloude = Instantiate(prefab_cloude); 121 122 cloude.transform.position = new Vector3(UnityEngine.Random.Range(-200, 200) / 100.0f, playerPos.y - 8, 1); 123 124 } 125 126 private void CreateCoin() 127 { 128 129 Vector3 playerPos = player.transform.position; 130 131 GameObject coin = Instantiate(prefab_coin); 132 133 coin.transform.position = new Vector3(UnityEngine.Random.Range(-200, 200) / 100.0f, playerPos.y - 8, 1); 134 135 } 136 137 private void CreateCabe() 138 { 139 140 Vector3 playerPos = player.transform.position; 141 142 int randomValue = UnityEngine.Random.Range(0, 3); 143 144 UnityEngine.Debug.Log(randomValue); 145 146 if (randomValue == 0) 147 { 148 GameObject cabe = Instantiate(prefab_cabe); 149 GameObject cabe2 = Instantiate(prefab_cabe); 150 151 cabe.transform.position = new Vector3(1.59f, playerPos.y - 8 - 0.625f, 0); 152 cabe2.transform.position = new Vector3(-1.59f, playerPos.y - 8 - 0.625f, 0); 153 } 154 else if (randomValue == 1) 155 { 156 GameObject cabe = Instantiate(prefab_cabe2); 157 158 cabe.transform.position = new Vector3(-0.86f, playerPos.y - 8 - 0.625f, 0); 159 } 160 else 161 { 162 GameObject cabe = Instantiate(prefab_cabe2); 163 164 cabe.transform.position = new Vector3(0.86f, playerPos.y - 8 - 0.625f, 0); 165 } 166 } 167 168 public void PlusCoin() 169 { 170 Coin++; 171 textCoin.text = Coin.ToString(); 172 173 } 174 175 public void SendScore() 176 { 177 UnityEngine.Debug.Log(6); 178 GameObject.Find("GameControler_Result").GetComponent<Result>().get_score(score, Coin); 179 UnityEngine.Debug.Log(7); 180 SceneManager.LoadScene("シーン_リザルト画面"); 181 } 182} 183 184-------------------------------------------------------- 185Resultスクリプト 186 187using System; 188using System.Collections; 189using System.Collections.Generic; 190using System.Diagnostics; 191using UnityEngine; 192using UnityEngine.UI; 193using UnityEngine.SceneManagement; 194 195public class Result : MonoBehaviour 196{ 197 198 public int score = 0; 199 private int coin = 0; 200 private int total_score = 0; 201 static private int [] high_score = { 0, 0, 0 }; 202 203 public Text Now_Score; 204 public Text Now_Coin; 205 public Text Now_Total_Score; 206 public Text High_Score1; 207 public Text High_Score2; 208 public Text High_Score3; 209 210 void Start() 211 { 212 213 } 214 215 void Update() 216 { 217 if (Input.GetMouseButtonDown(0) || Input.GetKeyDown("left") || Input.GetKeyDown("a") || Input.GetMouseButtonDown(1) || Input.GetKeyDown("right") || Input.GetKeyDown("d")) 218 { 219 SceneManager.LoadScene("シーン_ゲーム開始画面"); 220 } 221 } 222 223 public void get_score(int a, int b) 224 { 225 score = a; 226 coin = b; 227 score_record(); 228 } 229 230 public void score_record() 231 { 232 total_score = score * (1 + coin); 233 if (total_score > high_score[0]) 234 { 235 high_score[2] = high_score[1]; 236 high_score[1] = high_score[0]; 237 high_score[0] = total_score; 238 } 239 else if (total_score > high_score[1]) 240 { 241 high_score[2] = high_score[1]; 242 high_score[1] = total_score; 243 } 244 else if (total_score > high_score[2]) 245 { 246 high_score[2] = total_score; 247 } 248 } 249}

試したこと

障害物はいくつか種類があるのですが(スクリプトの内容はおおむね同じ)、どの障害物にあたってもうまくいきません

GameControl_Hiruスクリプト内のSendScoreスクリプトを以下のようにしてどこまでちゃんと動いているか確認したところ、
public void SendScore()
{
UnityEngine.Debug.Log(6);
GameObject.Find("GameControler_Result").GetComponent<Result>().get_score(score, Coin);
UnityEngine.Debug.Log(7);
SceneManager.LoadScene("シーン_リザルト画面");
}
デバッグログには6しか表示されませんし、当然シーンも変更されませんでした

public void SendScore()
{
UnityEngine.Debug.Log(6);
//GameObject.Find("GameControler_Result").GetComponent<Result>().get_score(score, Coin);
UnityEngine.Debug.Log(7);
SceneManager.LoadScene("シーン_リザルト画面");
}
としてゲームオブジェクトに触る部分をコメントアウトした場合、6,7ともに表示されシーンも変更されます
よって良くない部分がこの部分であると判断しました

public void SendScore()
{
UnityEngine.Debug.Log(7);
GameObject Go = GameObject.Find("GameControler_Result");
UnityEngine.Debug.Log(8);
Go.GetComponent<Result>().get_score(score, Coin);
UnityEngine.Debug.Log(9);
SceneManager.LoadScene("シーン_リザルト画面");
}
一行で横着したのが良くないと思い以上のようにオブジェクトに触る部分を変更した場合、7,8まで表示されます

‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
恐らくシーンをまたぐのが良くないと思い同じシーン内にTryというスクリプトを新たに作り、それをGameControler_Hiruオブジェクトにアタッチしてみました
Tryスクリプトの内容は以下です
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Try : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{

} // Update is called once per frame void Update() { } public void sample1(){ Debug.Log(10); }

}

そのうえでGameControl_Hiruスクリプトを以下のように変更しました
public void SendScore()
{
GameObject.Find("GameControler_Hiru").GetComponent<Try>().sample1();
SceneManager.LoadScene("シーン_リザルト画面");
}
この場合、デバッグログにちゃんと10と出てくれますし、シーン変更もされます

上記から恐らく別シーンのスクリプトに触れるのに問題が発生していると考えます
scoreをstaticにして橋渡ししても、結局別シーンのスクリプトに触れない問題は解決されないので、あくまでメソッドを使ってスコアを橋渡ししたいと思っています

とても初歩的な内容だとは思いますが、やり方をご教授願えればと思います

補足情報(FW/ツールのバージョンなど)

UnityはUnity 2019.4.11f1 (64-bit)というものを使用しています

https://drive.google.com/file/d/15xqjHUvS_VX4qWAgGutcG4wTh6vxsds_/view?usp=sharing
ファイルは上記リンクからダウンロードできます
どうぞよろしくお願いします

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

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

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

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

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

guest

回答1

0

ベストアンサー

GameObject.Findメソッドですが現在ロードしているシーンの中からオブジェクトをサーチするみたいですね。複数シーンをロードしている場合はすべてのシーンが対象になりますが、ロードしてないシーンのオブジェクトはサーチできないようです。

https://docs.unity3d.com/ja/current/ScriptReference/GameObject.Find.html
Note: If the game is running with multiple scenes then Find will search in all of them.

シーンの加算、アンロードするなども参考になるかと思います。

C#

1 SceneManager.LoadScene("OtherSceneName", LoadSceneMode.Additive);

https://docs.unity3d.com/ja/2019.4/ScriptReference/SceneManagement.SceneManager.UnloadSceneAsync.html

ちなみにシーンを完全に読み込むのに少し時間がかかるのでOnLoadイベントに登録しておいて次のシーンを呼び出せば登録したメソッドがロード完了したあとに実行されます。
以下参考のコードです。

C#

1public class SendTest : MonoBehaviour 2{ 3 void OnSceneLoaded(Scene scene, LoadSceneMode mode) 4 { 5 if(scene.name == "Receive"){ 6 Send(); 7 } 8 } 9 10 [ContextMenu("send")] 11 public void Send(){ 12 GameObject.Find("Receive").GetComponent<ReceiveTest>().Receive(); 13 //GameObject.Find("GameControler_Result").GetComponent<Result>().get_score(score, Coin); 14 } 15 16 [ContextMenu("NextScene")] 17 public void NextScene(){ 18 SceneManager.sceneLoaded += OnSceneLoaded; 19 SceneManager.LoadScene("Receive"); 20 } 21}

ただ、シーンロード先のメソッドを呼び出すよりも他の方法を検討してみてはどうでしょうか?
質問者さんは今のシーンのスコアデータを次のシーンで使いたい感じなので

案1,シングルトンを使う
次のシーンに移動してもスコアデータのオブジェクトは残す
以下が参考になります。
https://unity.moon-bear.com/unitychan-coin/singleton-gamemanager/

案2,PlayerPrefsを使う
スコアを保存しておいて、次のシーンで保存したスコアを呼び出す
https://www.sejuku.net/blog/69991

案3,ロード後シーン先のメソッドを呼び出す
今回のOnSceneLoadedに登録するしてロード後に呼び出す方法

別案、スコア表示UIを表示非表示させる
スコアを表示させるだけであればシーンを移動しなくてもUIで表示非表示してもいい
GameControl_HiruでUIを作成してスコアを表示させる

どの方法を使うかは好みにも状況にもよります。
別案で問題なければシーンをまたぐ必要がありません
どうしてもシーンをまたぐ必要があるなら案1を使う人が多いようです。
以上参考まで

投稿2020/11/14 06:49

編集2020/11/14 07:01
newBee

総合スコア82

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問