Missing reference exception

Hi guys,
i got stucked for hours and still haven’t found how to fix this problem. The first time when i play the game there are no problems but when i restart the level i get this error on the “RespawningSystem” script. When the player dies, game over ui display and two buttons are shown: restart or respawn. The player can use respawn one time (an ad start playing) and after that he respawn. When he die again only the restart button is shown. All this works one time but when he restart the level i get this error on the respawn script. I searched inside the inspector and nothing is missing and nothing is destroyed. I also tried to debug the reference to this script and the first time is correct, all the next instead are null and that’s why the error. But i can’t figure out why i lose the reference to this script if inside the inspector i see the reference correctly
Do you have any ideas? thanks :slight_smile:

Not trying to sound rude, but how exactly do you expect us to help you without knowing the error message or even the script throwing the error. Preferably both, with matching code lines and formatted properly.

Yeah sorry, i avoided to post the code cause is a bit messy. This is the error:
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.
UnityEngine.GameObject.GetComponent[T] () (at :0)
RewardedAdsButton.Respawn () (at Assets/Scripts/RewardedAdsButton.cs:75)
RewardedAdsButton.OnUnityAdsDidFinish (System.String placementId, UnityEngine.Advertisements.ShowResult showResult) (at Assets/Scripts/RewardedAdsButton.cs:44)
UnityEngine.Advertisements.Platform.Platform+<>c__DisplayClass41_0.b__0 () (at Library/PackageCache/com.unity.ads@3.4.5/Runtime/Advertisement/Platform/Platform.cs:171)
UnityEngine.Advertisements.Utilities.CoroutineExecutor.Update () (at Library/PackageCache/com.unity.ads@3.4.5/Runtime/Advertisement/Utilities/CoroutineExecutor.cs:17)

This is the script for the respawn and is attached to an empty object

public class RespawnSystem : MonoBehaviour {

    public GameObject gameOverScreen;
    public GameObject respawnButton;
    public Transform player;
    public Text timerText;



    public void Respawn() {

        StartCoroutine("Resuming");
       
    }


    IEnumerator Resuming() {

        int timer = 3;
     
        timerText.gameObject.SetActive(true);

        gameOverScreen.SetActive(false);
        player.position = Vector3.zero;
        player.GetComponent<Animator>().Play("Idle");

        while (timer > 0) {
            timerText.text = timer.ToString();
            timer--;
            yield return new WaitForSecondsRealtime(1f);
        }

        GameController.instance.gameOver = false;
        player.GetComponent<Rigidbody2D>().isKinematic = false;
        Time.timeScale = 1;
        GameController.instance.isRespawned = true;
        respawnButton.SetActive(false);
        timerText.gameObject.SetActive(false);

    }

}

This is the script for the ad and is attached to the respawn button. OnClick of the respawn button calls ShowAd()

public class RewardedAdsButton : MonoBehaviour, IUnityAdsListener {

    private bool testMode = true;
    private string googlePlayID = "11111111";
    private string myPlacementId = "rewardedVideo";

    public GameObject respawnSystem;

    void Start() {

        Advertisement.AddListener(this);
        Advertisement.Initialize(googlePlayID, testMode);
       
    }


    public void ShowAd() {

        if (Advertisement.IsReady(myPlacementId)) {

            Advertisement.Show(myPlacementId);

        }
        else {

            //Debug.Log("AD not ready");

        }

    }


    // Implement IUnityAdsListener interface methods:
    public void OnUnityAdsDidFinish(string placementId, ShowResult showResult) {
        // Define conditional logic for each ad completion status:
        if (showResult == ShowResult.Finished) {
            // Reward the user for watching the ad to completion.
            Respawn();
        }
        else if (showResult == ShowResult.Skipped) {
            // Do not reward the user for skipping the ad.
        }
        else if (showResult == ShowResult.Failed) {
            Debug.LogWarning("The ad did not finish due to an error.");
        }
    }

    public void OnUnityAdsReady(string placementId) {
        // If the ready Placement is rewarded, show the ad:
        if (placementId == myPlacementId) {
            //Advertisement.Show(myPlacementId);
       
        }
        
    }

    public void OnUnityAdsDidError(string message) {
        // Log the error.
    }

    public void OnUnityAdsDidStart(string placementId) {
        // Optional actions to take when the end-users triggers an ad.
    }

    private void Respawn() {
      
        respawnSystem.GetComponent<RespawnSystem>().Respawn();

    }


}

I really dont wanna be that guy, but your code lines dont match between error and script :smile:
You need to include the using directives at the top of the file. Basically, the entire file. Otherwise the lines in the error wont match the script, as it is for example the case in your 4’th error line, where it references ‘(at Assets/Scripts/RewardedAdsButton.cs:75)’. Line 75 does not exist, and presumably corresponds to line 67 in the posted example since that’s the only line in Respawn(). However this makes tracking down the problem unnecessarily complicated.

Edit: From what i can tell so far, the RespawnSystem script does not appear in the error, so it’s probably not the culprit. The error occurs in Respawn() in the RewardedAdsButton script. There is only one line in that function ‘respawnSystem.GetComponent().Respawn()’. First of all, i assume you only use this respawnSystem (of type GameObject) to reference the RespawnSystem component. If that’s the case, you could change the type from GameObject to RespawnSystem, assign it through the inspector like usual and skip the GetComponent part. Currently, there are two references that could be missing, the GameObject ‘respawnSystem’ itself, or the RespawnSystem component on it. The error implies it’s the GameObject, but i’m not familiar enough with Unity specific errors to know if there are any quirks. So for now let’s check what’s actually going on by checking which of these objects doesnt exist anymore:

// Inside Respawn() function
if(!respawnSystem){
    Debug.Log("GameObject is null");
}
if(respawnSystem && !respawnSystem.GetComponent<RespawnSystem>()){
    Debug.Log("Component is null");
}
respawnSystem.GetComponent<RespawnSystem>().Respawn();

Then check which message you see in the console while the problem ocurs.

It says that the gameobject is null. This is strange because from the inspector nothing is missing. I’ll attach the screens
5827117--617731--Desktop Screenshot 2020.05.10 - 00.25.17.96.png 5827117--617737--Desktop Screenshot 2020.05.10 - 00.25.38.91.png

5827117--617731--Desktop Screenshot 2020.05.10 - 00.25.17.96.png

I tried a thing. Instead of showing the ad, i tried to call Respawn() after the button respawn is clicked. Like this:

public void RespawnTest() {

        respawnSystem.Respawn();

    }

I tested it and always work, no errors. I can respawn, die, restart, respawn without any problems. So probably something is wrong with the AD. I’m realy newbie with them, today is the first time that i try to use them

I tested more and find another thing. I get the error only when i reload the scene
For example if i show the ad and then call respawn (case with inifinite respawning time):

  1. Works the first time, show the ad, player respawn correctly
  2. Player dies again, press button, show ad, respawn correctly
  3. Repeat number 2, always works
    Now this time when the player can respawn just 1 time
  4. Works the first time, show the ad, player respawn correctly
  5. Player dies again, there isn’t the button for respawning (i disable it after the first use), player restart the game (i reload the scene)
  6. Player dies, there is the button for respawning (new game), show the ad, the error of missing reference pops out.
    I tried the same cases without showing the ad (like the precedent answer) and always works, error never pops out
    I’m really lost

Sorry for another answer but i tested again. I can’t sleep without fixing this. It seems that if i attach the script “RewardedAdsButton” on a empty object and not on the respawn button it always works. So inside the onclick of the button i put the reference to this empty object with the script and i don’t get the error and it’s working. What is going on? Could it be a bug?

It’s certainly a bug. As a rule of thumb, i doubt it’s a Unity bug tho.

Cant you just do that then for now?

Do you use any DontDestroyOnLoad()s or any other things that cause the second load of the scene to not be identical to the first time it was loaded? If it always works the first time you load a scene, and then you reload the identical scene and it always causes a reproducible problem, then the two scenes must not be truly identical. Sadly i’m neither an expert on Ads nor Loading, so this goes towards uncharted territory for me. How are you loading the new scene? There are different loading modes, so maybe something messes up because some data is kept or something. Even tho, this is purely speculative right now.

I dont use dontdestroyonload, i just reload the scene with

public void Restart() {

        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);

    }

This function is inside the GameController script. This script on Awake has

void Awake(){
       
        if (instance == null){
        
            instance = this;

        }
        else if (instance != this) {
            
            Destroy(gameObject);

        }
       
    }

Where instance is a static GameController variable. If something is wrong with reloading the level, wouldn’t the error always pops out? The strange thing that this happen only when the ad is showed

The bug is an inherent effect of how you do things chronologically.
You haven’t shown the code for reloading scenes, but it’s pretty obvious what happens.

When you reload the scene, the current scene will be destroyed entirely, except for some objects that are meant to stay alive. Before you continue adding DontDestroyOnLoad everywhere, don’t do that… It’s something that needs to be used carefully, because it’s well known for introducing bugs.

Anyway, what happens is that you attached a listener to the ad-system that you’re using. The listener happens to be a component in your case, which exists in your scene.
When you reload the scene, the listening component is destroyed, but it’s still registered as a listener to the Ad System. And that’s the problem. You keep a reference to something that is destroyed (which is okay as long as you do not access Unity API). Thus, the callback works as expected (no specific API call), BUT the field you’re then trying to access is a GameObject which lived in the scene that’s been unloaded, which means that one was destroyed, too. You cannot do that, that’s also what the exception message says. You’re trying to access something that’s been destroyed.

2 Likes

So to avoid this error i can simply attach the script on an empty object and not on the button? (as answered before)

Not sure what you mean, but that’d probably be same situation.

Check this

Available “globally”:

  • Advertisement

    Available in Scene:
  • RespawnSystem (component, but referenced as GameObject)
  • RewardedAdsButton (component, registered as listener to the Advertisement API)

First time the scene is loaded:

  • RespawnSystem_1
  • RewardedAdsButton_1 has a reference to RespawnSystem_1 (via its GameObject)
  • Advertisement has a reference to RewardedAdsButton_1

When you reload, the old scene is unloaded (object instances are destroyed) and the object instances will be re-created.

  • RespawnSystem_2
  • RewardedAdsButton_2 has a reference to RespawnSystem_2 (via its GameObject)
  • Advertisement still has a reference to RewardedAdsButtons**_1** (listener not removed) and potentially RewardedAdsButton_2 (as this will be registered as well).

Note the listeners registered for the advertisement… There’s still the old component registered, and that one will be destroyed (C++ side, and Unity’s C# API knows that as well) when you reload, but due to the nature of C#, the C# object still exists and can be referenced and can be called into, as long as nothing Unity-specific happens.

The method “OnUnityAdsDidFinish” is just a normal method enforced by the interface “IUnityAdsListener”. So it can still be called, even when the component was destroyed in the context of Unity, but that part is still “alive” and accessible.

Also, the fields of the button instance are still there… I mean, the whole instance still exists. And one of the field is the RespawnSystem field. That one referenced RespawnSystem_1 and “still does”, but it’s been destroyed, there’s only the C# object left. And the pure C# side of a destroyed GameObject instance is completely inaccessible, as Unity detects any call to it. That’s why it throws an exception when you call GetComponent or anything else on it.

You need to remove listeners that you added to Advertisement, and that should happen when you either don’t need them anymore, or when an object is destroyed.
Depending on the order of loading, showing ads etc, you may fix the issue by handling ads, then unsubscribe when the button is destroyed, then reload the scene.

If that’s not the order you desire, e.g. you want to show ads while re-loading in order to shorten the waiting time, then you’ll need to put more effort in it and you’ll need to do things a little differently.

Thanks you all, i’ll try to fix that way

Just in case if someone faced the same situation.
The problem is in
Advertisement.AddListener(this); somewhere in Start() method;
Just remove the listener by Advertisement.RemoveListener(this); when rewarded Ad is shown (for example in OnUnityAdsDidFinish) and that’s it.

2 Likes

Oh thank you, it solved my problem