C# first level must be beat twice to get to level 2 for unknown reasons

Upon completion of the first level, it loads the first level again. About completion of the first level for the second time, it loads the second level. Cannot fathom why. Posted is my code for the GameManage which tells when levels need to be loaded. Any help or guidance would be very appreciated.

using UnityEngine;
using System.Collections;
//added new using
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour {
    public static int currentLevel = 0;
    public static int coinCount;
    public static int totalCoinCount;
    public GameObject coinParent;
    private static bool winScreen = false;
    private static bool loseScreen = false;
    public int winScreenWidth, winScreenHeight;
    public int loseScreenWidth, loseScreenHeight;
    //
    public GUISkin skin;
    public GUIStyle GUIStyle;
    public Font f;
    //
    void Start()
    {
        totalCoinCount = coinParent.transform.childCount;
        if (PlayerPrefs.GetInt ("Level Completed") > 0) {
            currentLevel = PlayerPrefs.GetInt ("Level Completed");
        } else {
            currentLevel = 0;
        }
    }
    public static void CompleteLevel()
    {
        winScreen = true;
    }
    public static void FailLevel()
    {
        loseScreen = true;
    }
    void LoadNextLevel()
    {
        Time.timeScale = 1f;
        if (currentLevel < 29)
        {
            currentLevel += 1;
            SaveGame();
            //SceneManager.LoadScene(string scenePath)
            SceneManager.LoadScene (currentLevel);
        }   
    }
    void SaveGame()
    {
        PlayerPrefs.SetInt("Level Completed", currentLevel);
    }
    public static void AddCoin()
    {
        coinCount += 1;
    }
    void OnGUI()
    {
        //
        GUI.skin.font = f;
        GUI.skin = skin;
        GUIStyle style = new GUIStyle(GUI.skin.button);
        style.fontSize = 35;
        if(winScreen)
        //  
        {
            coinCount = 5;
            Rect winScreenRect = new Rect(Screen.width/2 - (Screen.width *.5f/2), Screen.height/2 - (Screen.height * .3f/2), Screen.width *.5f, Screen.height *.3f);
            GUI.Box(winScreenRect, "Level Completed", style);

            if(GUI.Button(new Rect(winScreenRect.x + winScreenRect.width - 420 ,winScreenRect.y + winScreenRect.height -200, 250, 150), "Continue", style))
            {
                LoadNextLevel();
                winScreen = false;
            }
            if(GUI.Button(new Rect(winScreenRect.x + winScreenRect.width - 420 ,winScreenRect.y + winScreenRect.height -40, 250, 150), "Quit", style))
            {
                SceneManager.LoadScene ("main_menu");
                winScreen = false;
                Time.timeScale = 1f;
            }
        }
        if(loseScreen)
        {

            Rect loseScreenRect = new Rect(Screen.width/2 - (Screen.width *.5f/2), Screen.height/2 - (Screen.height * .5f/2), Screen.width *.5f, Screen.height *.5f);
            GUI.Box(loseScreenRect, "You were melted =(", style);
         if(GUI.Button(new Rect(loseScreenRect.x + loseScreenRect.width - 570 ,loseScreenRect.y + loseScreenRect.height -180, 250, 150), "Continue", style))
             {
                if(currentLevel == 0)
                {
                SceneManager.LoadScene ("level1");
                loseScreen = false;
                Time.timeScale = 1f;
                } else {
                    SceneManager.LoadScene (currentLevel);
                    loseScreen = false;
                    Time.timeScale = 1f;
                }
            }
        }
    }
}

Lines 56 and on.

It’s probably either one of the following:

#1: your scene with index 0 is not the first level
#2: if it’s not #1, your first level might be registered twice

To elaborate my thoughts on option #1:
First level is loaded when you enter the game from the menu (menu possible being the scene with index 0)
So in level one (which is scene with index 1), reading player prefs the first time before anything has completed returns 0. Next, you complete the level, increment the variable that’s supposed to keep track of the level index so it’s 1 now => loading the “next level” will be level with index 1, so the same level again.

It’s essentially the inconsistent treatment of the value:
The saved value - at least from the name you’ve given that entry in the PlayerPrefs - represents the completed level, hence you use 0 before anything has completed.
However, in your code you treat that value as being the ‘currentLevel’ that has to be played, hence the level is off by one.

That’d be more obvious if you renamed the currentLevel variable to completedLevel. You’d immediately see that you increment completedLevel by one, save it - which is correct - and load the completedLevel again - which is no longer correct.

1 Like

Thank you for your feedback man!!! I am going to give this a try :slight_smile:

You are right. The main menu is index0. So when the first level (index1) is completed, it increments to index1 which is still level one which causes level 1 do be replayed even after completion. I can either change this statement below by adding an if statement (I think so?). I was thinking that, when on the main menu, hitting new game would increment the index. This is also another viable option. Posted below is the statement after winning a level. The second post is my main menu script.

if(GUI.Button(new Rect(winScreenRect.x + winScreenRect.width - 420 ,winScreenRect.y + winScreenRect.height -200, 250, 150), "Continue", style))
            {
                LoadNextLevel();
                winScreen = false;
            }

using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;

public class MainMenu : MonoBehaviour {
    public GUISkin skin;
    public GUIStyle GUIStyle;
    public Font f;
    void OnGUI ()
    {


        GUI.skin.font = f;
        GUI.skin = skin;
        GUIStyle style = new GUIStyle(GUI.skin.button);
        style.fontSize = 35;
        GUI.Label (new Rect (Screen.width/3, Screen.height/8, 1000, 1000), "Ice Crew");
        if(PlayerPrefs.GetInt("Level Completed") > 0)
           {
            if(GUI.Button(new Rect (Screen.width/2.5f,Screen.height/1.5f,200,100), "Play", style))
        {   
            SceneManager.LoadScene(PlayerPrefs.GetInt("Level Completed"));
        }
        }
        if(GUI.Button(new Rect (Screen.width/2.5f,Screen.height/2.5f,200,100), "New Game", style))
        {   
            PlayerPrefs.SetInt("Level Completed", 0);
            SceneManager.LoadScene(1);
        }
    }   
}

Let me know what your thoughts are on this please.

It’s still confusing with the current naming.
If you want to handle it that way, why don’t you just save it as “next level” or something. Then you can simply start out with value 1.

Also, is there any particular reason for using the legacy UI? The new UI is much better and you won’t need all that code to draw the elements.

I’d strongly recommend constants for the names of the entries in the player prefs. But that’s just a small improvement.

1 Like

Ok I’m working on the new menu now. I am revamping a game I made 2-3 years ago. Didn’t bother to update the menu yet.

Also, I’ll change the variable names to make it easier to understand. I’ll get back to you asap!

Ok so I took out the main menu script and made the new menu with the new UI and a script to go along with each buttons function. I still haven’t figured out why level one plays twice. You said to start out with value 1. I assume You mean to change public static int currentLevel = 0; to = 1. I did and the level still plays twice so I changed it back. What else can I try? I’m not sure what I can change in the naming to make it less confusing. Also, can you please elaborate on what you mean by recommending constants for the names of the entries in the player prefs. I’m not sure what you mean. I reposted my game manager code in this post. Thank you!

using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
    public static int currentLevel = 0;
    public static int coinCount;
    public static int totalCoinCount;
    public GameObject coinParent;
    private static bool winScreen = false;
    private static bool loseScreen = false;
    public int winScreenWidth, winScreenHeight;
    public int loseScreenWidth, loseScreenHeight;
    void Start()
    {
        totalCoinCount = coinParent.transform.childCount;
        if (PlayerPrefs.GetInt ("Level Completed") > 0) {
            currentLevel = PlayerPrefs.GetInt ("Level Completed");
        } else {
            currentLevel = 0;
        }
    }
    public static void CompleteLevel()
    {
        winScreen = true;
    }
    public static void FailLevel()
    {
        loseScreen = true;
    }
    void LoadNextLevel()
    {
        Time.timeScale = 1f;
        if (currentLevel < 29)
        {
            currentLevel += 1;
            SaveGame();
            SceneManager.LoadScene (currentLevel);
        }    
    }
    void SaveGame()
    {
        PlayerPrefs.SetInt("Level Completed", currentLevel);
    }
    public static void AddCoin()
    {
        coinCount += 1;
    }
    void OnGUI()
    {
        if(winScreen)
        {
            coinCount = 0;
            Rect winScreenRect = new Rect(Screen.width/2 - (Screen.width *.5f/2), Screen.height/2 - (Screen.height * .3f/2), Screen.width *.5f, Screen.height *.3f);
            GUI.Box(winScreenRect, "Level Completed");

        if(GUI.Button(new Rect(winScreenRect.x + winScreenRect.width - 420 ,winScreenRect.y + winScreenRect.height -200, 250, 150), "Continue"))
            {
                LoadNextLevel();
                winScreen = false;
            }
            if(GUI.Button(new Rect(winScreenRect.x + winScreenRect.width - 420 ,winScreenRect.y + winScreenRect.height -40, 250, 150), "Quit"))
            {
                SceneManager.LoadScene ("main_menu");
                winScreen = false;
                Time.timeScale = 1f;
            }


        }
        if(loseScreen)
        {

            Rect loseScreenRect = new Rect(Screen.width/2 - (Screen.width *.5f/2), Screen.height/2 - (Screen.height * .5f/2), Screen.width *.5f, Screen.height *.5f);
             GUI.Box(loseScreenRect, "You were melted =(");
         if(GUI.Button(new Rect(loseScreenRect.x + loseScreenRect.width - 570 ,loseScreenRect.y + loseScreenRect.height -180, 250, 150), "Continue"))
             {
                if(currentLevel == 0)
                {
                    SceneManager.LoadScene ("level1");
                loseScreen = false;
                Time.timeScale = 1f;
                } else {
                    SceneManager.LoadScene (currentLevel);
                    loseScreen = false;
                    Time.timeScale = 1f;
                }
            }
        }
    }
}

No, I wasn’t talking about the static field directly, rather about the initial value of the entry in the PlayerPrefs.

The PlayerPrefs’ getter methods also allow you to specify a default value that will be returned if the key has not yet been added.
I recommend to save the index of the next level, as this probably fits your current code the most.
So your start method might look like:

private void Start()
{
    totalCoinCount = coinParent.transform.childCount;
    currentLevel = PlayerPrefs.GetInt("Next Level", 1); // if the entry does not exist, return 1 as default, as we have to start with level 1
}

Now it’ll start out with 1, and when you hit LoadNextLevel, the script should work as is, because you increment the value first (so it’ll be 2), then save it and load the level with that exact index (level 2).
That’s one way to solve this, but I had already explained that earlier.

If you want to keep saving the value as “level completed”, you’d simply need to increment the loaded value by one before you use it to load the next level.

In regards to the constants, whenever you use literal values such as the “Completed Level” string, you’d be better of declaring it as constant, for example:

private const string LEVEL_COMPLETED = "Level Completed";

and use it like

 ... = PlayerPrefs.GetInt(LEVEL_COMPLETED);

If you do that throughout your code, you can change the key easily in that one single line and there won’t be any inconsistencies or typos in your code.

1 Like

It worked I <3 you.

There will be a special thanks shoutout to you in this game once it is released on play store and App Store.