Options and Back Scene Switching Works ONLY ONCE in a Row

If I start the game playing in the Start Screen scene, the Options button works (it takes me to the Options Screen scene). But in the Options Screen, the Back button doesn’t work.

However, if I start the game playing in the Options Screen scene, the Back button works (it takes me to the Start Screen scene). But then in the Start Screen, the Options button doesn’t work. Also, the Start Game button won’t work at this point either.

I can’t switch back and forth in and out of the Options screen.

I think I have all the code the same. Any ideas of what could be causing this, or how to track this bug?

Thanks,
Jim

Hi Jim-2,

Are there any error messages in your console when you press the Options button in the Start Screen scene a second time? Maybe it is not connected with a method in its OnClick field anymore.

Hi Nina,

Thanks for getting back so fast.

There are no errors in the Console, which is surprising (that was the first place I looked).

However, after a 2 hour break, I’m doing a bit more testing now.

The OnClick()'s both have methods when the game is not running, and each one has a method if I run the game from that scene, but I just found out, that when I click through (i.e. if I run the game from the Start Screen, and click to get to the Options Screen), the second scene is missing it’s OnClick() method (from which ever scene I start the game from).

As a test, I ran the game from the Start Screen. Before I clicked on the Options button, it looked like this:
image
After I clicked on the Options button, it went to the Options Screen, and it looked like this:
image
And, not surprisingly, the Back button doesn’t work.
I stopped the game, and with the game not running, and the Options Screen looks like this:
image

So, it appears to have removed the OnClick() method when it loaded the scene. Which ever scene is loaded by the game (the second scene) is missing it’s OnClick() method.

The singleton pattern appears to be working, since the LevelLoader is listed under DontDestroyOnLoad, but just in case I’ve missed something, here’s that code:

    private void Awake()
    {
        // SINGLETON PATTERN:
        int gameStatusCount = FindObjectsOfType<LevelLoader>().Length;
        if (gameStatusCount > 1)
        {
            gameObject.SetActive(false);
            Destroy(gameObject);
        }
        else { DontDestroyOnLoad(gameObject); } // if there is not one already, don't destroy "when the level loads in the future" - Rick Davidson.
    }

If you have any other ideas, or if you have more questions, please let me know.

Thank you,
Jim

The problem is very likely related to the “singleton”. Maybe there are two LevelLoader scripts in your scene.

For simplicity, you could reference the LevelLoader prefab from your Assets folder in the OnClick field. Since the relevant methods do not depend on anything in the scene, referencing the prefab could work.

Hi Nina,

I’ve got the first working. I removed the Level Loader GameObject, and replaced it with a prefab of the Level Controller, on both the Start Screen and the Options Screen, and they now go back and forth between them. :smiley:

However, if I click on Start Game it goes to the game, after the game ends, the Try Again and Main Menu buttons, for the Lose case, are not working. Their OnClick() methods are missing then, too.

The prefabs don’t have them on it, and Overrides > Apply All is not updating them (with Level Lost Canvas GameObject highlighted in the Hiearchy, and doing this in the Inspector), and I think it should - am I missing something here?

Thank you,
Jim

Hi Nina,

Thank you. In Level 1, there appears to be only one LevelLoader.cs - I right clicked on the LevelLoader.cs file in Assets\Scripts > Find References In Scene. It found, one, plus a ref. on each of the buttons (Main Menu, and Try Again), so I think there is only one there.

However,I think I found 2 of something a while ago, so I may have gotten something screwed up by having 2. I will continue to look for this tomorrow. I will also keep studying the prefabs.

Also, my LevelController.cs does “not” have a Singleton. Is that correct?

Also, the LevelController is missing from under Level Related Game Objects, when Level 1 is reloaded (when I click on Play Again). If you know why this might be happening, that would be great, too.

Thank you,
Jim

Should be correct. If the LevelController component is attached to the “only” LevelLoader game object, it won’t get destroyed either due to DontDestroyOnLoad(gameObject);.

Hi Nina,

I found a second LevelLoader.cs script in Level 1. I removed that. I think that was causing part of the problem. It’s working much better, thank you.

The prefabs are still not keeping the OnClick() methods. If I start the game from Level 1, the Try Again button is working when the game is lost, and multiple times in a row - this is good. The Main Menu button is working, taking me to the Main Menu. And Start Game is working, taking me to the game again. However, when the game is lost now, the buttons have lost their OnClick() methods. And the prefabs don’t seem to keep the OnClick() methods - I have been unable to get them to keep them, anyway.

If the LevelController component is attached to the “only” LevelLoader game object, it won’t get destroyed either due to DontDestroyOnLoad(gameObject); .

I think this is where the problem (or one problem) might be. I see I have this, without a Level Loader Game Object:
image
with this on the Level Controller GameObject:

I’ll keep working on it. I think I’ve got something still off here.

Thanks,
Jim

The prefab is in your Assets folder, isn’t it? Make sure you assigned the prefab to the OnClick field of the button. This works only if the prefab does not depend on anything in the scene.

To ensure we are talking about the same, here’s the definition of the Unity terms:

  • prefab = the game object in your Assets folder
  • game object = the game object in the Hierarchy (being connected to a prefab, doesn’t make it a prefab)

Hi Nina,

Thank you for all your help. I’ve got a lot more of it working now.

The prefab is in your Assets folder, isn’t it?

Yes, in a sub-sub-folder:
image

Make sure you assigned the prefab to the OnClick field of the button. This works only if the prefab does not depend on anything in the scene.
I believe I have done this, but I messed around with the prefabs, deleting them in the Hierarchy, and dragging them back in from the Assets subfolder. I’m understanding much more of this, but I think I still have one or two more things to learn. I read the 8 subsections of Prefabs in the Unity Manual (not the Scripting one). I’ll review those again in the future. I have 2 questions right now:

  1. How can I tell if a prefab depends on something in the scene? I think I’ve been mixing this up so some parts of prefabs were not loading when it loaded a scene for the second time.

  2. On what is probably a different topic, I have this warning:
    image
    Line 22 is in the Singleton (code above):

 else { DontDestroyOnLoad(gameObject); } // if there is not one already, don't destroy "when the level loads in the future" - Rick Davidson.

Do you know why it is telling me this?

Thank you again. The course is excellent. I’ve just gotten things a little tangled near the end of this detailed module (Glitch Garden), but I’ve learned tons of valuable stuff. I’m really looking forward to TileVania - once I finish this course, I’m going back to work on the 2D platformer I already started.

I’m off to bed now, so I won’t get your reply until tomorrow, but I will check it then.

Thank you,
Jim

Usually, Unity throws a warning because we are not allowed to reference game objects in prefabs. If you missed the warning, the exposed field in the Inspector of the prefab remains “empty”. And if you try to access the “empty” variable to execute things on a non-existent objects, you would get a NullReferenceException.

Do you know why it is telling me this?

I don’t know but I assume that the “singleton” component is assigned to a child game object instead of a top level game object. DontDestroyOnLoad must not be called on children because a child gets destroyed along with its parent. If the parent is not “persistent”, there would be a conflict between the “persistent” child and Unity’s rule that all non-persistent game objects get destroyed when a new scene gets loaded.

A top level game object would be “Level Related Game Objects”, a child “Level Controller”. It might well be that “Level Controller” gets destroyed along with “Level Related Game Objects” when a new scene gets loaded. Since the new game objects share the same name, it might look as if they were the same.

Hi Nina,

Usually, Unity throws a warning because we are not allowed to reference game objects in prefabs. If you missed the warning, the exposed field in the Inspector of the prefab remains “empty”. And if you try to access the “empty” variable to execute things on a non-existent objects, you would get a NullReferenceException.

Thank you. I think I have done all 3 of those :blush:: Unity warning, field remaining empty, and the NullReferenceException. I’m obviously exploring all the possibilities to learn as much as possible :grinning:.

And thank you for the singleton info. This was very helpful. The singleton was definitely on a child object. I moved it up, and the warning went away.

I think I understand how the singleton and the reference variables work. I’m just not sure how I should implement this to fix the last problem, without making bigger changes to the code that I probably should - I’m not too concerned about getting it perfect, I just want it good enough that I can go through the last 8 lessons and learn what I can from them.

This last problem is that the LevelController script is not turning the win/lose Canvases (Level Complete Canvas, and Level Lost Canvas) on and off. I.e. If either or both of these Canvases are enabled when the level starts, the LevelController should set them to false and turn them off, but the Canvases stay enabled. And, not surprisingly, if I disable them in the inspector, the LevelController script doesn’t turn them on at the end of the level.

If I leave the Level Lost Canvas enabled, the menu buttons work. It’s just that the LevelController script is not able to turn them off and on.

I would like to get this working, because it’s obviously (now obvious to me) all related to the above stuff. Here’s what I have:
image
And here’s what the Inspector shows for the LevelController:

If you can help me with this last problem, that would be awesome!

Thank you,
Jim

As it seems, this is indeed just a issue with references. If you draw the logic with pen on a sheet of paper, you’ll very likely be able to check and fix the “links” between the objects yourself. The problem in cases like these is usually that the data is “scattered” and not visible at first view. This makes it difficult to see the underlying logic (or the attempt to realise a logic). That’s not your fault, and you cannot do anything to change that because that’s a “problem” of how information is visualised in Unity.

That being said, try to make the “Level Related Game Objects” a child of the “Level Controller” game object. Maybe that’ll fix the problem. If it does not, note down your goal and try to develop a solution (not implementantion!) of how it is supposed to work.

For instance: If the “Level Controller” game object is persistent (due to the LevelController component), are the “Level Complete Canvas” and “Level Lost Canvas” required to be persistent as well? If so, one could exploit the fact that the child of a persistent game object does not get destroyed automatically either when a new level gets loaded.

I think the underlying problem is: “LevelController” references the Canvas objects in this scene. However, if the level gets reloaded or another level gets loaded, it’s a new scene. The referenced Canvas objects would get destroyed even though it looks as if they are still there in the Hierarchy. However, those in the Hierarchy are new Canvas objects. And, of course, your LevelController object does not suddenly reference those new Canvas objects. The computer is “stupid”. You always have to tell it exactly what you want it to do. So you either have to make the LevelController look for new Canvas objects if the WinLabel and the LoseCanvas variables are null (no object reference), or you make the Canvas objects (indirectly) persistent as well.

Hi Nina,

Thank you that was extremely helpful.

So you either have to make the LevelController look for new Canvas objects if the WinLabel and the LoseCanvas variables are null (no object reference), or you make the Canvas objects (indirectly) persistent as well.

That’s what I was thinking, but I wasn’t sure if that was all the possibilities, so thank you for confirming this.

I’ve now done the next lesson, “Set and Save Music Volume.” Making the Music Player, and the error with DontDestroyOnLoad(this), that made a bunch of MusicPlayers, when we (Rick, and I) switched in and out of the scene, was actually quite enlightening.

I think I understand how it works now (yea!, the course is working). I just need to use this understanding, like you said to make a logic drawing, and trace it. I will continue with this tomorrow.

Thank you,
Jim