1

I have code that looks like this.

public static Dictionary<int, Action> functionsMap;
void Function()
{
    if (!isDictionaryInitialized)
    {
        functionsMap = new Dictionary<int, Action>();

        functionsMap.Add(1, () => StartCoroutine(Function1()));
        functionsMap.Add(1, () => StartCoroutine(Function2()));
    }
 }

void CheckForFunction()
{
    var r = currentFunctionNumber;

    if (functionsMap.TryGetValue(r, out currentAction)) { currentAction(); }
}

The code works fine when I start my program. However if I go to another scene and then return to it, I get this error.

"MissingReferenceException: The object of type 'ScriptName' 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."

The problem is I have never destroyed the object. Initially I didn't have bool isDictionaryInitialized and I defined the new Dictionary outside of the Function because I thought the error was related to my program trying to access a Dictionary that was deleted after the scene was closed. I get the same problem with or without the bool, and regardless of where I define the Dictionary.

What is causing this, and what is the reason so I can avoid making the same mistake?

Edit: This question was marked as duplicate, but the link I don't believe applies to my situation, or if it does I don't understand how. It says static objects are not reloaded on a scene change, and the Dictionary is defined as a static object. I also tried changing it to non-static and the result is the same.

I have dozens of gameobjects in my code and don't have this issue with any other object, so I assume the problem is related to how the dictionary object is defined. Is there a way to keep the Dictionary object from being destroyed on scene change? I don't have it as a game object in the scene, it's just defined in the code itself as a public static Dictionary. Could someone tell me what I need to do differently please and thank you?

NomadicBinary
  • 67
  • 1
  • 2
  • 8
  • Possible duplicate of [Loading scene causes scripts to stop working](https://stackoverflow.com/questions/44191957/reloading-scene-causes-scripts-to-stop-working) – Ruzihm Nov 20 '18 at 17:07

2 Answers2

1

The problem might be caused by changing scenes because simply loading another scene would destroy all gameobject in the current scene.

According to Object.DontDestroyOnLoad.

The load of a new Scene destroys all current Scene objects.

To solve this, you can either use DontDestroyOnLoad function to mark the object you want to be kept before loading another scene, or using different way to load like LoadSceneMode.Additive without destroying the current scene.

ming060
  • 302
  • 3
  • 6
  • Thank you, I'm familiar with DontDestroyOnLoad but doesn't that have to be applied to a gameobject? How can I apply DontDestroyOnLoad to a public static Dictionary? Using DontDestroyOnLoad(functionsMap) doesn't work. Or do I need to declare it as a gameobject within the script with public gameobject = functionsMap, or something like that? – NomadicBinary Nov 20 '18 at 17:41
  • After your Edit, I found that the dictionary is not a gameobject. And my solution won't be applied. I'm guessing that you are using a static variable to keep the reference of the dictionary. And you must initialize the dictionary in a script that attached to a gameobject and assign it to the static variable. Can you post how you do it . It would be helpful. – ming060 Nov 20 '18 at 17:48
  • I edited my original question, but i have it declared as public static Dictionary functionsMap; at the beginning of the class. Then I call the function in the original post periodically when the scene is open. I was going to just define all of the keys when declaring the function, but that causes errors with other parts of my code because I use IEnumerators and apparently I can't reference IEnumerators in a Monobehavior class from the error I received. I'm open to other ways to solve the problem, it's not necessary for the dictionary to be defined after the scene starts – NomadicBinary Nov 20 '18 at 17:56
  • Since the variables are constant, I'm just new to using Dictionary objects in C# so I'm unsure the proper way to do it. I know it's a lot easier than I'm making it, I just don't know the proper syntax. – NomadicBinary Nov 20 '18 at 17:57
  • Can you explain more on how you call the Function() and how it gets executed? Does the class you define attached to any gameobjects in the scene? – ming060 Nov 20 '18 at 17:59
  • Sorry, you must not attach the class to a gameobject because it's not a gameobject. I want to know more about how you initialize the dictionary. – ming060 Nov 20 '18 at 18:03
  • I declare the dictionary before run-time, then when I start the scene I run the function in the original post which initializes the dictionary and sets a bool isDictionaryInitialized, so that if the function is called again it doesn't try to initialize the dictionary a second time (Otherwise I get an error that the key already exists). I then have another function CheckForFunction that is called periodically every few seconds, and it uses a randomly generated number to determine which action is used from the Dictionary. – NomadicBinary Nov 20 '18 at 18:44
  • Do you call the Function from another script which is attached to a gameobject ? – ming060 Nov 20 '18 at 19:11
  • I use the Update function in the script to listen for a bool that's toggled by another script. When that happens, the script runs CheckForFunction and references the dictionary keys. So the function isn't directly called from another script, no. – NomadicBinary Nov 20 '18 at 19:19
  • Is it possible the error is caused by executing null reference of currentAction? I'm guessing the dictionary object still exists but the currentAction does not since the gameobject has the function already been destroyed. – ming060 Nov 20 '18 at 22:15
0

First you are adding two Actions to your Dictionary but both with the same key.

functionsMap.Add(1, () => StartCoroutine(Function1()));
functionsMap.Add(1, () => StartCoroutine(Function2()));

wouldn't this overwrite the first entry?


Than your general problem is:

While functionsMap is static the two actions / one action you added are non-static (StartCoroutine always requires an instance of MonoBehaviour to run on) and therefore only available for the according component! (You talked about an Update method so this has to be a MonoBehaviour attached to a GameObject in your first scene.)

Now if you change the Scene, the GameObject holding that component is probably destroyed if you are not using DontDestroyOnLoad

The result is that your static Dictionary stays intact and filled meaning the entry with key = 1 still exists but the Value namely the added non-static Action is no longer available.

To avoid that you should add

DontDestroyOnLoad(this.gameObject);

either in Awake or Start to prevent the GameObject holding the component and thereby holding the Action that belongs to the reference(s) in the entry in your Dictionary.

derHugo
  • 83,094
  • 9
  • 75
  • 115