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

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

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

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

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

Q&A

解決済

1回答

2411閲覧

参照が入っているのにMissingReferenceExceptionが発生する

SeiSeis

総合スコア12

C#

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

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

0グッド

1クリップ

投稿2020/10/21 13:41

編集2020/11/01 12:17

現在Unityでゲームを開発しています。

Awake()でGetComponent<Rigidbody>()してRigidbodyを取得しているPlayerLocomotorというクラスがあるのですが、

c#

1 2public class PlayerLocomotor : MonoBehaviour 3{ 4 [SerializeField] 5 private Rigidbody rb = null; 6 7 private void Awake() 8 { 9 currentBoostRemain = boostMax; 10 SystemManager.IsIngameSwitch += SetIsIngame; 11 rb = GetComponent<Rigidbody>(); 12 }

シーンをSceneManager.LoadScene(SceneManager.GetActiveScene().name)で読み込んだ際、1週目では起こらなかったMissingReferenceExceptionがこのrb変数に対して必ず発生しています。

private修飾子なので外部から変更されることはないと思うのですが、このスクリプト内でもrbを勝手に削除する処理はありません。

イメージ説明
となると怪しいのはこの1番下のSystemManager.SetRigidbody()ですが、たどれるだけたどっても直接このコンポーネントを破棄するような処理はありません。

c#

1 ///public static class SystemManager 2 public static void SetRigidbodyList(Rigidbody rb) 3 { 4 gameSystemManager.SetRigidbodyList(rb); 5 }

c#

1 ///public class GameSystemManager: MonoBehaviour 2 public void SetRigidbodyList(Rigidbody rb) 3 { 4 rigidBodyList.Add(rb); 5 gravityWall.SetGravityList(rb); 6 }

c#

1 ///public class GravityWall : MonoBehaviour 2 public void SetGravityList(Rigidbody rb) 3 { 4 rigidBodyList.Add(rb); 5 }

イメージ説明
イメージ説明

挙句、シーンを再読み込みした後のインスペクターを確認するとrigidbodyがちゃんと割り当てられています。
イメージ説明

ほかにはどういう対応ができるでしょうか。どなたかよろしくお願いします。

2020/10/21追記:
rbにプロパティを付けてシーン再読み込み時にset/getが正しく行われているかを検証してみました。

c#

1 [SerializeField] 2 private Rigidbody rb = null; 3 private Rigidbody RB { set { Debug.Log("passSet", rb.gameObject); rb = value; } get { Debug.Log("passGet", rb.gameObject); return rb; } }

イメージ説明
ちゃんとエラー前にSetしているようです。ちなみに手前のsetはgetcomponent<>によるものでした。

2020/10/25追記:

c#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.UI; 5 6 7[RequireComponent(typeof(Rigidbody))] 8public class PlayerLocomotor : MonoBehaviour 9{ 10 [SerializeField] 11 private Rigidbody rb = null; 12 private Rigidbody RB { set { Debug.Log("passSet", rb.gameObject); rb = value; } get { Debug.Log("passGet", rb.gameObject); return rb; } } 13 14 [SerializeField] 15 private float moveOnGroundPower = 100f; 16 [SerializeField] 17 private float moveInAirPower = 100f; 18 19 [SerializeField] 20 private float airForwardPower = 6; 21 22 private float airForwardPowerCurrent = 0; 23 public float airForwardPowerMAX = 200f; 24 25 private const float floatingSpeed = 7f; //システム的に前に進むスピード 26 27 [HideInInspector] 28 public bool isBoosted = false; 29 private bool isBoostedOld = false; 30 31 [SerializeField] 32 private Slider boostGauge = null; 33 private const float boostMax = 100f; //ブースト燃料のマックス 34 private float currentBoostRemain = 100; //ブースト燃料の残り 35 private const float boostCost = 10f; //ブーストするのに必要な燃料コスト。コスト毎秒必要 36 private const float boostRecoverValue = 30f; //燃料の自動回復速度。値毎秒回復する 37 38 public ParticleSystem boostParticle = null; 39 40 public bool isCoroutine = false; 41 42 private bool isParalyzed = false; 43 private Vector3 paralyzedVector = Vector3.zero; //麻痺した時の方向ベクトル 44 45 public bool isBurst = false; 46 47 private bool isIngame = false; 48 49 private void Awake() 50 { 51 currentBoostRemain = boostMax; 52 SystemManager.IsIngameSwitch += SetIsIngame; 53 RB = GetComponent<Rigidbody>(); 54 } 55 56 private void Start() 57 { 58 SystemManager.SetRigidbodyList(RB); 59 SystemManager.OnIsGroundSwicthed += OnGroundSwitched; 60 UpdateBoostUI(); 61 62 } 63 64 private void Update() 65 { 66 if (!isIngame) 67 return; 68 69 UpdateBoost(); 70 UpdateBoostUI(); 71 UpdateParalyze(); 72 73 if (isBoosted) 74 airForwardPowerCurrent = Mathf.Lerp(airForwardPowerCurrent, airForwardPowerMAX, Time.deltaTime * airForwardPower); 75 else 76 airForwardPowerCurrent = Mathf.Lerp(airForwardPowerCurrent, 0, Time.deltaTime * airForwardPower); 77 } 78 79 public void MoveGround(Vector2 vector) 80 { 81 UpdateBoostOnGround(); 82 83 var vec = new Vector3(vector.x * moveOnGroundPower, 0, vector.y * moveOnGroundPower); 84 AddForce(vec, ForceMode.Acceleration); 85 86 UpdateRotationOnGround(vec); 87 88 //var gVec = new Vector3(vec.x, 0, vec.z); 89 //SystemManager.PlayerMove(gVec); 90 } 91 92 public void MoveInAir(Vector2 vector) 93 { 94 UpdateBoostInAir(); 95 if (currentBoostRemain <= 0) 96 return; 97 98 var vec = new Vector3((vector.x * moveInAirPower), (vector.y * moveInAirPower * 2.0f), airForwardPowerCurrent); 99 AddForce(vec, ForceMode.Acceleration); 100 101 UpdateRotationOnAir(vec); 102 UpdateBoostParticle(vector.y); 103 104 var gVec = new Vector3(vec.x, vec.y, vec.z - (transform.position.z < 0 ? SystemManager.gravityPower + floatingSpeed : 0)); 105 SystemManager.PlayerMove(gVec); 106 } 107 108 public void AddForce(Vector3 vec, ForceMode mode = ForceMode.Acceleration) 109 { 110 if (isParalyzed) 111 return; 112 113 RB.AddForce(vec, mode); 114 } 115 116 private void UpdateRotationOnGround(Vector3 lookAtLocal) 117 { 118 if (isParalyzed) 119 return; 120 121 var vec = lookAtLocal.normalized; 122 vec += transform.position; 123 124 transform.LookAt(vec); 125 } 126 127 private void UpdateRotationOnAir(Vector3 lookAtLocal) 128 { 129 if (isParalyzed) 130 return; 131 132 var vec = lookAtLocal.normalized; 133 134 transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(vec), moveInAirPower * Time.deltaTime); 135 } 136 137 private void UpdateBoost() 138 { 139 if (isParalyzed) 140 return; 141 142 isBoosted = (TouchUtility.GetTouch() == TouchInfo.Moved || TouchUtility.GetTouch() == TouchInfo.Stationary); 143 144 145 if (currentBoostRemain <= 0) 146 isBoosted = false; 147 148 if (isBoosted != isBoostedOld) 149 { 150 isBoostedOld = isBoosted; 151 if(!SystemManager.GetIsGrounded()) 152 RB.useGravity = !isBoosted; 153 } 154 } 155 156 private void OnGroundSwitched(bool isGrounded) 157 { 158 if (isParalyzed) 159 return; 160 161 if (isGrounded) 162 RB.useGravity = true; 163 else 164 RB.useGravity = !isBoosted; 165 } 166 167 private void UpdateBoostOnGround() 168 { 169 currentBoostRemain += boostRecoverValue * Time.deltaTime; 170 if (currentBoostRemain > boostMax) 171 currentBoostRemain = boostMax; 172 } 173 174 private void UpdateBoostInAir() 175 { 176 if (isParalyzed) 177 { 178 if (boostParticle.isEmitting) 179 SetBoostParticleActive(false); 180 return; 181 } 182 183 if (TouchUtility.GetTouch() == TouchInfo.Moved || TouchUtility.GetTouch() == TouchInfo.Stationary) 184 { 185 currentBoostRemain -= boostCost * Time.deltaTime; 186 if (currentBoostRemain < 0) 187 currentBoostRemain = 0; 188 189 if (isBurst) 190 currentBoostRemain = boostMax; 191 192 if (!boostParticle.isEmitting && currentBoostRemain > 0) 193 SetBoostParticleActive(true); 194 } 195 else if (boostParticle.isEmitting) 196 SetBoostParticleActive(false); 197 } 198 199 private void SetBoostParticleActive(bool active) 200 { 201 if (active) 202 boostParticle.Play(); 203 else 204 boostParticle.Stop(true, ParticleSystemStopBehavior.StopEmitting); 205 } 206 207 private void UpdateBoostUI() 208 { 209 var v = Mathf.InverseLerp(0, boostMax, currentBoostRemain); 210 boostGauge.value = v; 211 } 212 213 public void TakeOffWithJump() 214 { 215 StartCoroutine(TakeOffCoroutine()); 216 } 217 218 private IEnumerator TakeOffCoroutine() 219 { 220 221 isCoroutine = true; 222 var boostedY = transform.position.y; 223 var destY = boostedY + 3; 224 225 yield return null; 226 227 while (transform.position.y < destY) 228 { 229 var vector = transform.forward * 2 + transform.up * 5; 230 231 AddForce(vector, ForceMode.Impulse); 232 yield return null; 233 } 234 AddForce(Vector3.forward * 50, ForceMode.Impulse); 235 236 isCoroutine = false; 237 } 238 239 public void SetParalyzeActive(bool active) 240 { 241 isParalyzed = active; 242 RB.useGravity = active; 243 244 if (active) 245 paralyzedVector = transform.rotation.eulerAngles; 246 } 247 248 private void UpdateParalyze() 249 { 250 if (!isParalyzed) 251 return; 252 253 SystemManager.PlayerMove(paralyzedVector.normalized); 254 } 255 256 private void UpdateBoostParticle(float inputYvec) 257 { 258 if (isBurst) 259 return; 260 261 var main = boostParticle.main; 262 main.gravityModifier = inputYvec; 263 } 264 265 public void AddBoost(float value) 266 { 267 currentBoostRemain = Mathf.Clamp(currentBoostRemain + value, 0, boostMax); 268 } 269 270 private void SetIsIngame(bool active) 271 { 272 isIngame = active; 273 } 274} 275

アドバイスを受けて、PlayerLocomotorクラスの全文を追記しました。
実行中に最初に起こるエラーとしてはこのクラス内のOnGroundSwitched()で、プレイヤーが設置していた場合の処理です。ゲーム開始時に設置判定が別クラスで行われるのでそれを受けてこの関数が実行されるのですが、ここでエラーが起こります。
同じクラス内での処理なので指摘していただいたような、シーンの更新によって破棄されたオブジェクトへのアクセスによるエラーというようなことはないと思うのですがほかに原因も思い当たりません。

2020/10/30追記:
ほかスクリプトのコードも載せてほしいとのことでしたが、文字数制限が来たためgithubにプロジェクトをアップロードしました。
GitHub
質問にあったスクリプトのパスを載せておきます
SystemManager.cs
SkyLow/TEST20200910/Assets/Scripts/Utility/SystemManager.cs

GameSystemManager.cs
SkyLow/TEST20200910/Assets/Scripts/Manager/GameSystemManager.cs

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

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

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

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

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

BluOxy

2020/10/21 14:34

>private修飾子なので外部から変更されることはないと思う SerializeField属性をつけている場合、インスペクタ上から変更できます
sakura_hana

2020/10/22 00:37

シーンを切り替えた際にオブジェクトが削除されますが、削除後も他オブジェクトから削除されたオブジェクト(のRigidbody)にアクセスし続けているとこのエラーが発生します。 その点に着目してコードを確認し直してみてください。
SeiSeis

2020/10/22 03:00

@sakura_hana なるほど。確かにそれはありえそうですね。 シーンを再読み込みしてもAwake()やStart()が呼ばれない、というようなことがあるなら確かにありえそうですが、そんなことがあるのでしょうか。。。
SeiSeis

2020/10/22 03:04

@BluOxy そうですね。インスペクタ上から変更できるということはプレイ中に手動で変更する以外に変更できるのでしょうか?それともインスペクタ上から変更をかけるという処理を何かスクリプトで行うことができるのでしょうか? ちなみにSerializeField属性を除いても症状は改善しませんでした。
sakura_hana

2020/10/22 04:45

Awake()やStart()が呼ばれないのではなく、呼ばれるタイミングの問題かと。 まず処理の実行順はこうです。 1.SceneManager.LoadSceneが呼ばれる 2.旧シーン上のPlayerLocomotorオブジェクトが削除される 3.新シーンがロードされ新しいPlayerLocomotorオブジェクト生成 4.新シーン上のPlayerLocomotorオブジェクトのAwake実行 5.SetRigidbodyList実行 そして「2の完了後〜5の前」まで「GameSystemManagerやGravityWallのrigidBodyListに入っている参照先」は存在しない(削除されている)ので、そこにアクセスしようとするとMissingエラーになります。 (PlayerLocomotorのrbは、PlayerLocomotorから見れば常時存在していますが、別クラスから見るとGameObjectごと削除されているのでアクセス出来ないことになります) GameSystemManagerやGravityWallがDontDestroyOnLoadで、常時rigidBodyListの中身を利用しているような場合に発生しやすいので確認してください。
SeiSeis

2020/10/30 00:24 編集

@sakura_hana なるほど。ただこの発生しているエラー、PlayerLocomotor内の関数なんですよね。 同じスクリプト内ならさすがにそういったものはないと思うのですが、原因が分からないです。。 シングルトンパターンのクラスやDontDestroyOnLoad()はありません。。
sakura_hana

2020/10/23 00:33

うーん、そうなると原因がよくわからないですね…… 一応PlayerLocomotorの全文と、どの部分でエラーが出ているか提示するとわかる方がいるかもしれません。
SeiSeis

2020/10/25 01:33

ありがとうございます。 分かりました。スクリプトの全文を追記してみます。
lazh

2020/10/25 15:15

かなり長くなってしまいそうですがSystemManagerとGameSystemManagerのスクリプトも欲しいです
SeiSeis

2020/10/30 11:46

文字数制限が来てしまったのでGitHubにプロジェクトをアップロードしました。 詳細を追記しておきます。
guest

回答1

0

自己解決

ようやく解決しました。
rigidbodyへとアクセスしていたエラー関数は静的なクラスのデリゲートによる関数でした。
つまり、デリゲートに格納されていたシーン破棄前のオブジェクトの関数を呼び出していたようです。
デリゲートに登録した関数を持つオブジェクトが破棄されても、デリゲート内の関数は問題なく実行されるんですね。。。
勉強になりました。
実質sakura_hanaさんのアドバイスが今回の核のようです。
回答に協力していただいた方々、この度はありがとうございました。

投稿2020/11/03 07:27

SeiSeis

総合スコア12

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問