Scene reloading while avoiding MissingReferenceException on action camera

Caveat: My solution may not fully work if we have multiple scenes but it works for this game so far.

I decided to implement scene reloading and some basic debugging tools after struggling with a few issues. My first attempt at scene reloading was working GREAT except it was triggering one very weird missing reference on the actionCameraGameObject within the CameraManager script.

Here are some important details on the MissingReferenceException

  1. Within the Camera Manager Start() and Awake() the reference is there and fine. It runs HideActionCamera() just fine for example.
  2. Within the hierarchy in editor the ActionVirtualGame object is there and the CameraManager script has a correct reference to it.
  3. The exception only triggers when referencing the actionCameraGameObject’s BaseAction_OnAny* subscribers. Unlike Awake and Start the actionCameraGameObject has a missing reference.

So it seemed there was something special going on with the objects referenced by the subscribers of static BaseAction events. None of the other event handling logic seems to exhibit this problem.

I initially implemented a workaround – call DontDestroyOnLoad(actionCameraGameObject); – within the CameraManager Awake() function. But this wasn’t the real fix. The problem was that old event logic was persisting for BaseAction.

Solution - subscribe to events in OnEnable and unsubscribe to events in OnDisable. All code samples at bottom.

It took me a while to figure this all out, but glad I did. I definitely learned something. I think also subscribing and unsubscribing this way may fix some race conditions we encountered with script execution order. Awake() can be used to setup the object then OnEnable() adds the subscribers. Start() and later code then can call Invoke() with confidence that all subscribers have been set up. I haven’t tried that yet, but plan to.


Before I made any fixes here were the exceptions:

MissingReferenceException: The object of type ‘GameObject’ 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.
CameraManager.BaseAction_OnAnyActionStarted (System.Object sender, System.EventArgs e) (at Assets/Scripts/CameraManager.cs:48)
BaseAction.ActionStart (System.Action onActionComplete) (at Assets/Scripts/Actions/BaseAction.cs:54)
ShootAction.TakeAction (GridPosition gridPosition, System.Action onActionComplete) (at Assets/Scripts/Actions/ShootAction.cs:160)
UnitActionSystem.HandleSelectedAction () (at Assets/Scripts/UnitActionSystem.cs:82)
UnitActionSystem.Update () (at Assets/Scripts/UnitActionSystem.cs:67)

MissingReferenceException: The object of type ‘GameObject’ 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.
CameraManager.HideActionCamera () (at Assets/Scripts/CameraManager.cs:26)
CameraManager.BaseAction_OnAnyActionCompleted (System.Object sender, System.EventArgs e) (at Assets/Scripts/CameraManager.cs:60)
BaseAction.ActionComplete () (at Assets/Scripts/Actions/BaseAction.cs:61)
ShootAction.NextState () (at Assets/Scripts/Actions/ShootAction.cs:83)
ShootAction.Update () (at Assets/Scripts/Actions/ShootAction.cs:57)

The code I’m running to reset the Scene is as follows. In the hierarchy I have a few simple UI elements copying a pattern similar to the TurnSystemUI. The important one here is the resetSceneButton.

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class DebugSystemUI : MonoBehaviour
{
    [SerializeField] private Button resetSceneButton;
    [SerializeField] private Toggle loggingToggle;

    public void Start()
    {

        resetSceneButton.onClick.AddListener(() =>
        {
            HandleSceneReset();
        });

        Debug.Log("Reset Button listerner added");

        loggingToggle.onValueChanged.AddListener(delegate
        {
            HandleLoggingMode(loggingToggle);
        });


    }

    private void HandleSceneReset()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

    private void HandleLoggingMode(Toggle toggle)
    {
        Debug.Log($"Confirm that toggle is set to: {toggle.isOn}");
    }
}

Within CameraManager add these and remove the code to subscribe from Start()

private void OnEnable()
    {
        BaseAction.OnAnyActionStarted += BaseAction_OnAnyActionStarted;
        BaseAction.OnAnyActionCompleted += BaseAction_OnAnyActionCompleted;
    }

    private void OnDisable()
    {
        BaseAction.OnAnyActionStarted -= BaseAction_OnAnyActionStarted;
        BaseAction.OnAnyActionCompleted -= BaseAction_OnAnyActionCompleted;
    }

Privacy & Terms