Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[Solved][C#]Dont destroy on load, this = null, Missing reference Exception

Discussion in 'Scripting' started by Svarr, Jan 6, 2016.

  1. Svarr

    Svarr

    Joined:
    Dec 20, 2015
    Posts:
    22
    After a very productive day, I got a new error yesterday.
    I thought I got the save/load functionality fixed but; I get a Missing reference exception every time I try to load if the scene is not loaded the first time. That means loading works when I start the game in the editor (loading also moves me to another scene), but when I enter the first scene again, I get the said error, claiming that the object of type 'SaveLoadController' has been destroyed. I don't get this when I try to save.
    SaveLoadController is the script that (obviously) contains the save and load functionality along with other related methods to prepare the data. This script is attached to a root empty object and applies a DontDestroyOnLoad on it while storing itself into a static variable.
    I don't have code anywhere which explicitly destroys the GameObject or the Script instance.
    Debug.Log lines show that after the first visit of the first scene this (called in the SaveLoadController script) becomes null, while the static variable is still SaveLoadController (SaveLoadController).

    Another thing to mention is, that as of now something either with saving or with loading is not working correctly. While saving, I store the indices items of the decklist have in the list "allcards" in an array, to not having to store the list of type "Card" (which didn't work). With this array, I can populate the decklist while loading. But right now when I load and view the decklist, I got the first entries of allcards not the cards I had saved. This may be just a side product to the above issue (or vice versa).

    Here is the script.
    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.IO;
    6. using System.Runtime.Serialization.Formatters.Binary;
    7. using CustomClasses;
    8.  
    9. public class SaveLoadController : MonoBehaviour
    10. {
    11.     public static SaveLoadController saverloader;
    12.  
    13.     void Awake()
    14.     {
    15.         if (saverloader == null)
    16.         {
    17.             saverloader = this;
    18.         }
    19.         else if (saverloader != this)
    20.         {
    21.             Destroy(gameObject);
    22.         }
    23.  
    24.         DontDestroyOnLoad(gameObject);
    25.     }
    26.  
    27.     public int[] PullCardCounts()        //    for saving games
    28.     {
    29.         int[] countarray = new int[CardsDB.allcards.Count];
    30.         int i = 0;
    31.         foreach (Card c in CardsDB.allcards)
    32.         {
    33.             countarray[i] = c.count;
    34.             i++;
    35.         }
    36.         return countarray;
    37.     }
    38.  
    39.     public int[] PullCardKillCounts()        //    for saving games
    40.     {
    41.         int[] kcountarray = new int[CardsDB.allcards.Count];
    42.         int i = 0;
    43.         foreach (Card c in CardsDB.allcards)
    44.         {
    45.             kcountarray[i] = c.killcount;
    46.             i++;
    47.         }
    48.         return kcountarray;
    49.     }
    50.  
    51.     public void PopulateCardCounts(int[] countarray)    //    for loading games
    52.     {
    53.         for (int i = 0; i == countarray.Length; i++)
    54.         {
    55.             CardsDB.allcards[i].count = countarray[i];
    56.         }
    57.         return;
    58.     }
    59.  
    60.     public void PopulateCardKillCounts(int[] kcountarray)    //    for loading games
    61.     {
    62.         for (int i = 0; i == kcountarray.Length; i++)
    63.         {
    64.             CardsDB.allcards[i].killcount = kcountarray[i];
    65.         }
    66.         return;
    67.     }
    68.  
    69.     /// <summary>
    70.     /// Pulls the win count.
    71.     /// </summary>
    72.     /// <returns>The window count.</returns>
    73.     public int[] PullWinCount()
    74.     {
    75.         int[] tmpArray = new int[AIControllerScript.allEnemies.Count];
    76.         int i = 0;
    77.         foreach (Enemy e in AIControllerScript.allEnemies)
    78.         {
    79.             tmpArray[i] = e.wincount;
    80.             i++;
    81.         }
    82.         return tmpArray;
    83.     }
    84.  
    85.     /// <summary>
    86.     /// Pulls the lose count.
    87.     /// </summary>
    88.     /// <returns>The lose count.</returns>
    89.     public int[] PullLoseCount()
    90.     {
    91.         int[] tmpArray = new int[AIControllerScript.allEnemies.Count];
    92.         int i = 0;
    93.         foreach (Enemy e in AIControllerScript.allEnemies)
    94.         {
    95.             tmpArray[i] = e.losecount;
    96.             i++;
    97.         }
    98.         return tmpArray;
    99.     }
    100.  
    101.    public int[] PullDeckContents()
    102.     {
    103.         int[] array = new int[CardsDB.DeckList.Count];
    104.         int h = 0;
    105.         foreach (Card c in CardsDB.DeckList)
    106.         {
    107.             array[h] = CardsDB.allcards.IndexOf(c);
    108.             h++;
    109.         }
    110.         return array;
    111.     }
    112.  
    113.     public void FillDeckList(int[] array)
    114.     {
    115.         for (int i = 0; i < array.Length; i++)
    116.         {
    117.             CardsDB.DeckList.Add(CardsDB.allcards[i]);
    118.         }
    119.     }
    120.  
    121.     public void FillWinCount(int[] array)
    122.     {
    123.         for (int i = 0; i < array.Length; i++)
    124.         {
    125.             AIControllerScript.allEnemies[i].wincount = array[i];
    126.         }
    127.     }
    128.  
    129.     public void FillLoseCount(int[] array)
    130.     {
    131.         for (int i = 0; i < array.Length; i++)
    132.         {
    133.             AIControllerScript.allEnemies[i].losecount = array[i];
    134.         }
    135.     }
    136.  
    137.     public void Load()
    138.     {
    139.         BinaryFormatter bf = new BinaryFormatter();
    140.         FileStream file = File.Open(Application.persistentDataPath + "/YourSaveFile.not", FileMode.Open);
    141.         GameData data = (GameData)bf.Deserialize(file);
    142.         file.Close();
    143.  
    144.         PopulateCardCounts(data.cardcountlist);
    145.         PopulateCardKillCounts(data.cardkcountlist);
    146.         FillLoseCount(data.losecount);
    147.         FillWinCount(data.wincount);
    148.         FillDeckList(data.decklist);
    149.  
    150.         Debug.Log(this.ToString());
    151.         Debug.Log("saverloader = " + saverloader.ToString());
    152.         PopUpControllerScr.ClosePopup();
    153.         PopUpControllerScr.sBuMidCap = "Close";
    154.         PopUpControllerScr.sTitle = "Information";
    155.         PopUpControllerScr.sContent = "Loading completed.";
    156.         PopUpControllerScr.ShowPopUp(false, false, true, false);
    157.         StartCoroutine(ScreenFadeInOut.fadeScript.EndScene("GameMenu")); // This is the line the exception points to
    158.     }
    159.  
    160.     public void Save()
    161.     {
    162.         BinaryFormatter bf = new BinaryFormatter();
    163.         FileStream file = File.Create(Application.persistentDataPath + "/YourSaveFile.not");
    164.         GameData data = new GameData();
    165.  
    166.         data.cardcountlist = PullCardCounts();
    167.         data.cardkcountlist = PullCardKillCounts();
    168.         data.losecount = PullLoseCount();
    169.         data.wincount = PullWinCount();
    170.         data.decklist = PullDeckContents();
    171.         bf.Serialize(file, data);
    172.         file.Close();
    173.  
    174.         PopUpControllerScr.ClosePopup();
    175.         PopUpControllerScr.sBuMidCap = "Close";
    176.         PopUpControllerScr.sTitle = "Information";
    177.         PopUpControllerScr.sContent = "Saving completed.";
    178.         PopUpControllerScr.ShowPopUp(false, false, true, false);
    179.     }
    180. }
    181.  

    Since the exception points to a line calling a co routine of another script, I'll add that as well, just in case.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3. using UnityEngine.UI;
    4. using System.Collections;
    5.  
    6. public class ScreenFadeInOut : MonoBehaviour
    7. {
    8.     public static ScreenFadeInOut fadeScript;
    9.     public float fadeTime = 1.5f;
    10.     private bool sceneStarting = true;
    11.     private Image myTextureComp;
    12.  
    13.     void Awake()
    14.     {
    15.         if (fadeScript == null)
    16.         {
    17.             fadeScript = this;
    18.         }
    19.         else if (fadeScript != this)
    20.         {
    21.             Destroy(gameObject);
    22.         }
    23.         DontDestroyOnLoad(gameObject);
    24.         myTextureComp = gameObject.GetComponentInChildren<Image>(true);
    25.     }
    26.  
    27.     void Update()
    28.     {
    29.         if (sceneStarting)
    30.         {
    31.             StartCoroutine(StartScene());
    32.         }
    33.     }
    34.  
    35.     void FadeToClear()
    36.     {
    37.         myTextureComp.CrossFadeAlpha(0f, fadeTime, false);
    38.     }
    39.  
    40.     void FadeToBlack()
    41.     {
    42.         myTextureComp.CrossFadeAlpha(1f, fadeTime, false);
    43.     }
    44.  
    45.     IEnumerator StartScene()
    46.     {
    47.         FadeToClear();
    48.         float waitTime = 0f;
    49.         while (waitTime < fadeTime)
    50.         {
    51.             waitTime += Time.deltaTime;
    52.             yield return null;
    53.         }
    54.         myTextureComp.enabled = false;
    55.         sceneStarting = false;
    56.         yield break;
    57.     }
    58.  
    59.     public IEnumerator EndScene (string newScene)
    60.     {
    61.         myTextureComp.enabled = true;
    62.         FadeToBlack();
    63.         float waitTime = 0f;
    64.         while (waitTime < fadeTime)
    65.         {
    66.             waitTime += Time.deltaTime;
    67.             yield return null;
    68.         }
    69.  
    70.         SceneManager.LoadScene(newScene);
    71.         sceneStarting = true;
    72.         yield break;
    73.     }
    74. }
    75.  

    And last but not least the missing reference exception:
    MissingReferenceException: The object of type 'SaveLoadController' 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.MonoBehaviour.StartCoroutine (IEnumerator routine) (at C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineMonoBehaviourBindings.gen.cs:60)
    SaveLoadController.Load () (at Assets/Scripts/SaveLoadController.cs:157)
    MainMenuControllerScript.CheckForLoad () (at Assets/Scripts/MainMenuControllerScript.cs:44)
    UnityEngine.Events.InvokableCall.Invoke (System.Object[] args) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:144)
    UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:621)
    UnityEngine.Events.UnityEventBase.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:756)
    UnityEngine.Events.UnityEvent.Invoke () (at C:/buildslave/unity/build/Runtime/Export/UnityEvent_0.cs:53)
    UnityEngine.UI.Button.Press () (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:35)
    UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:44)
    UnityEngine.EventSystems.ExecuteEvents.Execute (IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:52)
    UnityEngine.EventSystems.ExecuteEvents.Execute[IPointerClickHandler] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:269)
    UnityEngine.EventSystems.EventSystem:Update()
     
  2. Svarr

    Svarr

    Joined:
    Dec 20, 2015
    Posts:
    22
    Help is much appreciated.
     
  3. Fajlworks

    Fajlworks

    Joined:
    Sep 8, 2014
    Posts:
    344
    Try rewriting the line of code like:
    Code (CSharp):
    1. saverloader.StartCoroutine(ScreenFadeInOut.fadeScript.EndScene("GameMenu"));
    Even though nothing looks suspicious with code, it seems like your Load() method was called on the newly created SaveLoadController that is destroyed. Further investigation is advised.

    I would also highly suggest implementing singleton scripts using:
    http://wiki.unity3d.com/index.php?title=Singleton

    Basically your calls look like:
    Code (CSharp):
    1. public class SaveLoadController : Singleton<SaveLoadController>
    2. {
    3.     // your code...
    4. }
    5.  
    6. void PerformLoad()
    7. {
    8.      SaveLoadController.Instance.Load();
    9. }
    and you don't have to worry dealing with multiple gameObjects and destroying the extra one. Do note this approach requires first call from script, I don't think it will work properly if you place it into the scene hierarchy via inspector Add Component button. But that is okay, since Singleton gameObjects should not be placed inside any scene because they should be available from anywhere/any scene.

    Hope this helps!
     
  4. Svarr

    Svarr

    Joined:
    Dec 20, 2015
    Posts:
    22
    That solved it. Thank you so much!! :D

    As for the singletons, I will keep an eye out for a tutorial, as I didn't really understand the wiki article. Although I'm not sure that it's worth the effort in this particular case (the save/load functionality is only used in two of the scenes), it will definitely be useful for other things.

    The other issue I mentioned was caused by a mistake I made and is now fixed.