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

Question MissingReferenceException after reloading a scene.

Discussion in 'Getting Started' started by XoetziX, Apr 7, 2021.

  1. XoetziX

    XoetziX

    Joined:
    Mar 16, 2021
    Posts:
    16
    Hello,

    I have a problem when reloading a scene, after clicking the restart button.
    A reference to my gameOverUI panel is suddenly null and I have no idea why.

    I try to make the szenario as clear as possible:

    1) 1st start of the scene/level, everything works fine. When reaching the game over status my GameManager calls:
    Code (CSharp):
    1. levelUIManager.ShowGameOverUI();

    2) That is how my LevelUIManager looks like.
    public GameObject gameOverUI; -> gets the reference by the editor
    upload_2021-4-7_22-47-20.png

    ShowGameOverUI() simply activates the panel as you see below.

    Code (CSharp):
    1. public class LevelUIManager : MonoBehaviour
    2. {
    3.     public PlayerMovement player;
    4.     public GameObject completeLevelUI;
    5.     public GameObject gameOverUI; [B]//<------------- THIS IS THE PROBLEMATIC REFERENCE AFTER RELOADING THE SCENE[/B]
    6.  
    7.     public Text txt_Distance;
    8.     public Text txt_currentSpeed;
    9.     private bool gameOver = false;
    10.  
    11.     private void Awake()
    12.     {
    13.         LogHelper.DebugMe(); [B]//<------- THIS IS CALLED IMMEDIATELY AFTER THE RESTART AGAIN (AS EXPECTED) AND AT THIS MOMENT gameOverUI STILL HAS A REFERENCE?![/B]
    14.     }
    15.  
    16.     void Update()
    17.     {
    18.         if (!gameOver)
    19.         {
    20.             txt_Distance.text = (player.transform.position.z.ToString("0") + " m");
    21.         }
    22.  
    23.         //show speed
    24.         float tmpSpeed = player.rigidBody.velocity.magnitude * 3.6f;
    25.         txt_currentSpeed.text = "Speed: " + tmpSpeed.ToString("0");
    26.  
    27.     }
    28.  
    29.     internal void ShowGameOverUI()
    30.     {
    31.         gameOverUI.SetActive(true); [B]// <------- THROWING THE MissingReferenceException - The object of type 'GameObject' has been destroyed but you are still trying to access it[/B]
    32.     }
    33. [...]
    34.  

    3) The user hits the restart button
    The GameManager calls:
    Code (CSharp):
    1.  
    2.         SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    and the level restarts


    4) All GameObjects are destroyed and instantiated again automatically, right?!
    So, at this point I would expect that the LevelUIManager gets all his references again (part 1) and keeps them (part 2)!
    Part 1 is achived. I debugged it and when Awake() is called after the reload, the gameOverUI has a reference.
    Part 2 somehow not. And exactly this is the curious problem!
    When the gameOverUI.SetActive(true) is called again (within ShowGameOverUI()) the Exception is thrown.
    "MissingReferenceException The object of type 'GameObject' has been destroyed but you are still trying to access it"

    I would be very happy if anyone can explain me, what happens here!
    Thanks in advance!
     
  2. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    Does your LevelUIManager happen to have a [DontDestroyOnLoad] tag applied to it? If so, that would mean it does not get destroyed and reinstantiated, meaning it doesn't get updated references to the new objects that were created with the scene reload. It's still trying to access the ones that were destroyed before the scene reloaded, and obviously they're gone now.

    If your manager is going to hang around and survive a scene reload, you need to have some sort of method you can call on it (or better yet, a subscription to the SceneManager "sceneLoaded" event) that finds new references to the elements you need to work with using GameObject.Find.
     
    henriquepro likes this.
  3. XoetziX

    XoetziX

    Joined:
    Mar 16, 2021
    Posts:
    16
    Thanks @Schneider21 for your reply!

    To answer your question first: No, I did not set the LevelUIManager to DontDestroyOnLoad.

    However, meanwhile - since I am sitting on the problem again since 2 hours... - I found out that the problem indeed has something to do with DontDestroyOnLoad.
    I use DontDestroyOnLoad only within and for my GameManager (Singleton) and if I comment out the line below <<1>> the problem does not occur anymore.

    Code (CSharp):
    1. public class GameManager : MonoBehaviour
    2. {
    3.     private static GameManager instance;  
    4.     private LevelUIManager levelUIManager;
    5.  
    6.     public static GameManager Instance
    7.     {
    8.         get
    9.         {
    10.             if (instance == null)
    11.             {
    12.                 instance = FindObjectOfType<GameManager>();
    13.                 if (instance == null)
    14.                 {
    15.                     GameObject go = new GameObject();
    16.                     go.name = "GameManager";
    17.                     instance = go.AddComponent<GameManager>();
    18.                     DontDestroyOnLoad(go);
    19.                 }
    20.             }
    21.             return instance;
    22.         }
    23.     }
    24.  
    25.     private void Awake()
    26.     {
    27.         if (instance == null)
    28.         {
    29.             instance = this;
    30.             DontDestroyOnLoad(this.gameObject);[B] //<<1>> If I comment out this line, the problem does not occur[/B]
    31.         }
    32.         else
    33.         {
    34.             Destroy(gameObject);
    35.         }
    36.      }
    37.  
    38. private void Start()
    39.     {
    40.         if (levelUIManager == null)
    41.         {
    42.             levelUIManager = FindObjectOfType<LevelUIManager>();[B]//<<2>>[/B] [B]I tried this in order to get the reference back, if it is missing[/B]
    43.         }
    44.  
    45.  
    46.  
    EDIT - I have deleted some already written lines, since I belive I have just understood one aspect.

    You mentioned that the GameManager (which is kept alive) needs to find its (destroyed) references again.
    What I already do within the Start() method is: levelUIManager = FindObjectOfType<LevelUIManager>() (see <<2>>)
    That is would you meant, right?
    Okay, with this command the GameManager gets the reference to the LevelUIManager (again).

    But why does the LevelUIManager loose the reference to the gameOverUI?
    Since the LevelUIManager gets instantiated again, all references should be set again, right?
    And actually, as I have written in my first post, this indeed happens. When debugging and setting a break point within the Awake() method of the LevelUIManager I can see that it has a reference to the gameOverUI.
    But magically is looses it some time afterwards?!? Very curious and I really do not understand it...
     
  4. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    I'm having a bit of difficulty following exactly how you have this all set up, so I'm just gonna kinda blabber about how references work for a bit.

    I assure you there's nothing magical about how objects in Unity (or any program, for that matter) get or lose references. If a reference is being lost, it's for a reason. In your case, we have a clear indication that it's not actually a missing reference (as in, not assigned) but rather the reference you do have is missing (you have a reference for something that no longer exists). It makes sense that it happens on scene reload, since as you mentioned everything not marked as DontDestroyOnLoad gets removed and re-added.

    Do you have your GameManager and LevelUIManager components attached to the same object, maybe? I think if I just had a bit clearer picture of how your scene was setup, I could figure this out. More screenshots would be helpful. You could also throw the project up into a git repo and I'd take a look at it that way, if you like.
     
  5. XoetziX

    XoetziX

    Joined:
    Mar 16, 2021
    Posts:
    16
    Yes, I know, but at the moment it somehow feels like magic ;)

    okay, sorry. I tried to draw a clear picture, but obviously not succesfully.

    Since I did not use a git repo so far (I only do the programming as a hobby), that would probably take a while.
    Hence, I give it another try with screenshots and try to upload both relevant classes here, so that you hopefully get a cleared picture.

    That is the Hierarchy of the level:
    upload_2021-4-8_18-11-56.png

    Nothing special at the GameManager here:
    upload_2021-4-8_18-12-40.png

    The LevelUIManager gets the references to the UI elements it shall work with:
    upload_2021-4-8_18-13-6.png

    Attached the two classes.

    Does it help this way? Otherwise I will have a look at how to add it to git the evening.
     

    Attached Files:

  6. Schneider21

    Schneider21

    Joined:
    Feb 6, 2014
    Posts:
    3,510
    My best guess is that the scene reload is changing guids for your various GameObjects, and the new instance of the LevelUIManager isn't getting a proper reference to the new instance of the GameOver object, since that assignment is made from the scene.

    If it were me, I would try making the GameOver object a prefab (it looks like it already is) that you instantiate when you need it, instead of being something that's just sitting in the scene waiting to turn on. That, or make all the UI elements children of the LevelUIManager. That might allow it to find its reference?
     
  7. XoetziX

    XoetziX

    Joined:
    Mar 16, 2021
    Posts:
    16
    Regarding your recommended workarounds:
    I would really like to know what goes wrong with the current approach, because if it actually should work, there must be some hidden mistake or whatever. If I ignore it now, it may strike me later again somewhere else.

    That's what I meant with "magic", because it gets the reference at the beginning. Look at this:

    The following debug logs:
    Code (CSharp):
    1.    
    2. public class LevelUIManager : MonoBehaviour
    3. ...
    4. private void Awake()
    5.     {
    6.         Debug.Log(">>> LevelUIManager - awake - " + gameOverUI);
    7.     }
    8.     private void Start()
    9.     {
    10.         Debug.Log(">>> LevelUIManager - start - " + gameOverUI);
    11.     }
    12.    public void ShowGameOverUI()
    13.     {
    14.         Debug.Log(">>> LevelUIManager - ShowGameOverUI - " + gameOverUI);
    15.         gameOverUI.SetActive(true);
    16.     }
    Lead to the follwing output:

    Code (CSharp):
    1.  
    2. >>> LevelUIManager - awake - GameOver (UnityEngine.GameObject)
    3. UnityEngine.Debug:Log (object)
    4. LevelUIManager:Awake () (at Assets/Scripts/LevelUIManager.cs:19)
    5.  
    6. >>> LevelUIManager - start - GameOver (UnityEngine.GameObject)
    7. UnityEngine.Debug:Log (object)
    8. LevelUIManager:Start () (at Assets/Scripts/LevelUIManager.cs:23)
    9.  
    10. >>> LevelUIManager - ShowGameOverUI - null
    11. UnityEngine.Debug:Log (object)
    12. LevelUIManager:ShowGameOverUI () (at Assets/Scripts/LevelUIManager.cs:45)
    13. GameManager:GameOver () (at Assets/Scripts/GameManager.cs:86)
    14. PlayerCollision:OnCollisionEnter (UnityEngine.Collision) (at Assets/Scripts/PlayerCollision.cs:20)
    15.  
    16. MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
    17. Your script should either check if it is null or you should not destroy the object.
    18. LevelUIManager.ShowGameOverUI () (at Assets/Scripts/LevelUIManager.cs:46)
    19. GameManager.GameOver () (at Assets/Scripts/GameManager.cs:86)
    20. PlayerCollision.OnCollisionEnter (UnityEngine.Collision collision) (at Assets/Scripts/PlayerCollision.cs:20)
    21.  
    22.  
    So, while the Awake and Start methods are called the reference is still there, but if the GameManager calls the ShowGameOverUI method it is gone!?!o_Oo_O

    Additionally I have just added
    "ShowGameOverUI();"
    to the Start method, so it does the same as the GameManager does later on.
    This works, the expected screen is displayed.

    What I also checked is whether I accidentely access the public GameObject gameOverUI; from outside the class with right click on it and "find all references", but nothing.

    That drives me crazy. How many hours I try to find the reason for this strange behaviour!! :mad::(
     
    Pajgla likes this.
  8. Pajgla

    Pajgla

    Joined:
    Apr 3, 2015
    Posts:
    6

    Did you manage to fix this? I am also struggling with this issue right now :(