個人的に制作しているカーレースゲームでコースセレクトとカーセレクトという2個のシーンがあるのですが、コースを選択してクルマを選択する際にMissingReferenceExceptionという謎の例外が発生しました。コースセレクトシーンで設定したアクションがキーマップに残った状態で発生するので、解決方法を記述していきます。
エラーの内容
エラーとしては次の2個が並んで表示されます。
MissingReferenceException: The object of type '<コース選択用のスクリプト>' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.Component.GetComponent[T] () (at <4746c126b0b54f3b834845974d1a9190>:0)
<コース選択用のスクリプト>.MoveHorizontal () (at Assets/Scripts/<コース選択用のスクリプト>.cs:104)
<コース選択用のスクリプト>.Right (UnityEngine.InputSystem.InputAction+CallbackContext context) (at Assets/Scripts/<コース選択用のスクリプト>.cs:128)
UnityEngine.InputSystem.Utilities.DelegateHelpers.InvokeCallbacksSafe[TValue] (UnityEngine.InputSystem.Utilities.CallbackArray1[System.Action
1[TValue]]& callbacks, TValue argument, System.String callbackName, System.Object context) (at Library/PackageCache/com.unity.inputsystem@1.4.4/InputSystem/Utilities/DelegateHelpers.cs:46)
MissingReferenceException while executing 'performed' callbacks of 'Menu/Right[/Keyboard/d,/Keyboard/rightArrow]'
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr)
原因と対策
私の場合、input systemに登録したマップに対してアクションを登録しているのですが、コースセレクトとカーセレクトの異なるスクリプトから同じマップの同じアクションに重複して登録を行っていました。次のコードをそれぞれのスクリプトに記述しており、例えば右矢印を押すとコースセレクト用スクリプトのRight()とカーセレクト用スクリプトのRight()が呼び出される状態になっていました。
// インプットシステムの設定
var action_map = input_action.FindActionMap("Menu", throwIfNotFound: true);
action_map["Right"].performed += Right;
action_map["Left"].performed += Left;
action_map["OK"].performed += OK;
action_map["Cancel"].performed += Cancel;
対策としては、本来であればキーマップを別に用意しておくのが良いかもしれません。ですが、メニュー画面でいちいち別のマップを用意するのも大変ですので、シーンを移動する直前にアクションの登録を解除する方法をとりました。
今回はOK()の中でSceneManager.LoadScene()を呼び出しましたので、このメソッド内で登録を解除して無事に解決しました。
void OK(InputAction.CallbackContext context)
{
// インプットシステムの設定
var action_map = input_action.FindActionMap("Menu", throwIfNotFound: true);
action_map["Right"].performed -= Right;
action_map["Left"].performed -= Left;
action_map["OK"].performed -= OK;
action_map["Cancel"].performed -= Cancel;
…中略…
var scene_loding_param = new LoadSceneParameters() { loadSceneMode = LoadSceneMode.Single, localPhysicsMode = LocalPhysicsMode.None };
SceneManager.LoadScene("<シーン名>");
}