Missing Reference Exception

So I got this null exception error, my image is destroyed but my script is still trying to access it


*But my UI(image) is put in the gameSession prefabs and it is still there and not being destroyed at all
this error only appeared once I moved on to my 2nd level and died. Nothing happens on the first level, but when I get to the 2nd level and die there, the game freezes.
When I double-click the error Unity sent me here… I have no idea what to do with these lines

Please help
I can link u my source code, maybe u guys can take a peek to see the whole picture? idk this error have been buggin me the last few days

This is Unity code, so you must do nothing

This error usually happens when you destroy a component instead of the game object, or when something like an event got subscribed, but never unsubscribed again. When the scenes change, the objects are destroyed, but the event binding still holds on to the reference and tries to do something on it.

Looking at the stack trace, it appears the image in question is something where the fillAmount is being set, but that image has been destroyed. I haven’t done this course so I don’t know where to point you, but you should find where this fillAmount is being set (possible the healh?) and make sure that when it is being destroyed, it unsubscribes any events and it destroys the game object instead of the image

that is exactly the healthBar in my code, so I have that same idea but idk how to unsubscribe it from everything before destroying the gameObject
Here is my gameSession code, so I assign the healthBar fillAmount to the image healthBar right from the start. Then I changed it depending on the playerCurrentLives, but how to unsubscribe it away from all other events and making game session destroy itself instead of destroying the Image?

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.SocialPlatforms.Impl;
using UnityEngine.UI;

public class GameSession : MonoBehaviour
{
    [SerializeField] float reloadSceneDelay = 2f;

    // Số máu, số coin của người chơi lúc bắt đầu
    [SerializeField] int playerStartingHealth = 3;
    [SerializeField] int playerStartingCoin = 0;

    // Số máu trong game Session
    [SerializeField] int playerCurrentLives;
    [SerializeField] int playerCurrentScore;

    // Số máu trong game Session
    [SerializeField] Image healthBar;
    // Số coin trong game Session
    [SerializeField] TextMeshProUGUI coinText;

    //[SerializeField] Image playerHealthBarUI;

    // Awake sẽ được gọi when this script is brought to life(push the play button) or when we reload the scene
    void Awake()
    {
        // Singleton Pattern

        // FindObjectsOfType sẽ là tìm ra một mảng tổng hợp tất cả những object đó
        int numGameSession = FindObjectsOfType<GameSession>().Length;

        if (numGameSession > 1)
        {
            Destroy(gameObject); // có nhiều gameSession hơn thì xoá đi 

        }
        else
        {
            DontDestroyOnLoad(gameObject);
        }
    }

    void Start()
    {
        playerCurrentLives = playerStartingHealth;
        playerCurrentScore = playerStartingCoin;

        // Hiện UI thanh máu dựa trên số playerCurrentLives trong Game Session
        healthBar.fillAmount = playerCurrentLives * 0.1f;


        // Hiện UI số coin = playerCurrentScore
        coinText.text = "000";
    }

    void Update()
    {
        

    }

    public void ProcessPlayerDeath()
    {
        if (playerCurrentLives > 1)
        {
            MinusLives();
        }
        else
        {
            // trừ nốt trái tim cuối cùng
            playerCurrentLives--;
            healthBar.fillAmount = playerCurrentLives * 0.1f;

            Invoke("ResetGameSession", reloadSceneDelay);
        }
    }

    public void AddToScore(int pointsToAdd)
    {
        playerCurrentScore += pointsToAdd;

        if (playerCurrentScore < 100)
        {
            coinText.text = "0" + playerCurrentScore.ToString();
        }
        else
        {
            // chỉ chạy một lần khi được gọi tới chứ nếu để trong update thì nó chạy theo frame
            coinText.text = playerCurrentScore.ToString();
        }
    }

    public void AddHealth()
    {
        if (playerCurrentLives < 10)
        {
            IncreaseLives();
            return;
        }
        else
        {
            return;
        }
    }

    void MinusLives()
    {
        // chỉ thao tác với playerCurrentLives, UI sẽ tự thay đổi theo
        playerCurrentLives--;

        // Hiện UI thanh máu dựa trên số playerCurrentLives trong Game Session
        healthBar.fillAmount = playerCurrentLives * 0.1f;

        StartCoroutine(ReloadLevels());
    }

    void IncreaseLives()
    {
        playerCurrentLives++;
        healthBar.fillAmount = playerCurrentLives * 0.1f;
        
    }

    IEnumerator ReloadLevels()
    {
        yield return new WaitForSecondsRealtime(reloadSceneDelay);


        // lấy ra index của scene hiện tại
        int sceneBuildIndex = SceneManager.GetActiveScene().buildIndex;

        // load lại scene đó sau chừng 2s
        SceneManager.LoadScene(sceneBuildIndex);
    }

    void ResetGameSession()
    {
        
        
        FindObjectOfType<ScenePersist>().ResetScenePersist();

        // Sau khi đã huỷ game Session cũ đi thì load ra scene đầu tiên
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); // assuming the first scene is always 0-indexed

        // Khi reset thì huỷ gameSession này đi để load lại cái mới ra,
        // k huỷ đi thì sẽ có 2 gameSession hoạt động cùng lúc và điều đó rất là không hay (lỗi)
        Destroy(gameObject);


       


    }


}

So, it will appear the problem is that you got a reference to an object that is not being persisted. The healthBar image is destroyed when the scenes change, but the GameSession is not and it still has a reference to the healthBar image from when it first loaded. Things that are referenced like this on persisted objects should persist themselves, or you should have some way of updating the reference. In this case the easiest will probably be to persist the healthBar image (and the coinText if it’s not)

Alright thanks for the tip, still can u please tell me how do i make the image persist? i cant link to a prefabs, making me unable to link that healthBar image prefabs with the healthBar, and cant make it a persist

i tried changin the type of healthBar to gameObject so that i could make it a prefabs and put it in the scenePersist… but i dont know how to acess the image Component inside a gameObject


Anyway to persist an image that is placed inside the asset(sprite) folder?

I was wrong. Looking at your new screenshot I can see that the healthBar is part of the GameSession and therefore persisted - everything in GameSession will be persisted.

Open the GameSession prefab. Inside there, make sure you drag the image from within the prefab onto the healthBar field in the inspector. It may already be the case, but I have seen cases where the image was referenced from outside the prefab and then - while it looks correct - it’s not and the referenced image gets destroyed.

1 Like


now that u mention it, i just found out that i have accidentally link to an image that will get destr as soon as the game change scene. Im sorry i still dont know how to make an image persist without making it a gameObject, since i cant link a gameObject(prefabbed healthBar image) onto the healthBar field in the inspector…

No. Forget about the sprite. You are completely misunderstanding what I am saying. You cannot make an image (sprite) in the folder persistent. That is not what I meant at all, and we have now already established that you don’t need to persist anything further.

Open the GameSession prefab in the prefab view. Then, grab HealthBarCurrent game object in the hierarchy and drop it on the Health Bar field on the GameSession game object. Make sure you do this in the prefab view.

okay okay sorry for misunderstood, is this what u meant?

i tried it out right away and indeed something has changed even tho it looks real identical

Yes, that’s exactly what I meant! You need to now go in your scenes and revert any overrides that may be there. The prefab has to reference the health bar from within itself

I dont get it, it still not working and now there is another error in my gameSession, similarly to the Image error :frowning:


Maybe there is something dead wrong in my code that i havent find out yet :sob:

You have race conditions you need to solve. When you get to the next level, your player finds a GameSession, but this is not the GameSession that will persist. It’s going to get destroyed in a moment but you now have a reference to it. Get the game session when you need it, not at the start of the player object. I know everyone is always talking about performance and you should cache stuff, but this is negligible and you can get away with it.

I see, but in the game session lecture, this is what Rick did


a singleton right? So now the logic that I need is telling the game session not to load in a new game session if there is already another game session existing, not loading it in and then destroying it away after mere moments right?
I just have some idea dont know if i should use it, maybe only reference to game Session when we need it?

void LevelUp()
{
    GameSession gameSession = FindObjectOfType<GameSession>();
    if (gameSession != null)
    {
        // Thing related to gameSession
    }
}

What happens when you load the next scene is that the GameSession from the previous scene exists, as well as a GameSession from this scene. The GameSession from this scene runs the above code and sees that there is already a GameSession so it destroys itself. This is the expected behaviour.
But your PlayerDeathDetector finds a GameSession before that code has executed, so it finds the one from this scene - the one that will get destroyed when the above code runs - and then caches a reference to that. Now that object gets destroyed and your PlayerDeathDetector now holds a reference to an object that was destroyed

1 Like

Yes. This is what I said above; get the game session when you need it

1 Like

i see your point, thank you for clarifying it out for me, now imma refactor my code a bit to see if is gud.
Anw, im still wonder… So let say I ref to my gameSession when the player dies, so do i need to add something in my code to unsubscribe that reference after it’s done running? or it will automatically reset when that gameSession got destroyed?

No this is fine. Subscribing/Unsubscribing is something we do to events. You don’t have events, so you don’t have to worry about it.

1 Like

Thank you a lot for your assists! I learned alot from this section of the course, including that race condition u just mentioned. Cant believe i’ve been struggling over the last few days just because of ONE line of code :grinning_face_with_smiling_eyes: Thanks a lot

1 Like

If you use Destroy in the context of your singleton, also add gameObject.SetActive(false); to the if-block. Otherwise, it might be that other objects are still able to find the ‘destroyed’ object because Destroy() destroys at the end of the frame. FindObjectOfType can find active objects only, hence gameObject.SetActive(false); could solve this problem.

1 Like

Another gud solution noted! Many thanks to both :smiley:

Privacy & Terms