Application.LoadLevel() question

When reading the description for Application.LoadLevel() it states that “When loading a new level all game objects that have been loaded before are destroyed.” Pretty straightforward I thought, but not as much as I wanted (or thought). With my current game I hope from actual level to actual level using this method. I figured that it would also destroy all data in the game but it does not. When progressing through the game the levels get messed up with the old data that appear in memory.

Is this the right behavor of the function? i.e. data I store in memory are no game objects?

The only reason some data would remain after LoadLevel() is if it lives in a static class that’s not on a GameObject, or on a Monobehaviour on a GameObject that was set with DontDestroyOnLoad(gameObject).

I actually ran into this problem recently so what I did was create an empty scene and load that instead of reloading my original level. I was able to see that my LevelManager object was not getting destroyed. Try it and see if anything appears in the empty scene.

1 Like

None of these is the in my game. The game has 3 scenes: Menus, Movies (still work in progress, completely empty aside from a textmesh stating it’s movies and waiting for a key to be pressed) & MainGame. In MainGame, I’m loading the level data though a game that’s linked on an empty GameObject. This one runs fine. When the level is over (either game over, level exit or completed) it checks if if should go back to Menus, load Movies or reload MainGame. If it’s the last then I agree that it could happen that data might ‘hang’ in memory. But the cases that I had my ‘hanging’ data was when I went out of the level and back to Menus, from there I selected to play the next level and Movies was loaded and then MainGame. No way there should be any data ‘hanging’ anywhere in this case…

Luckily my issue isn’t overly big either. As said, all that’s ‘hanging’ are the data of the level, which is just an array of roughly 25x25. It’s pretty easy to clear it by calling a small routine, which I have done already…

Where does your 25x25 array of data live?

Only in the MainGame’s main procedure. I’m calling it from other procedures within MainGame, but that should not affect the ‘hanging’ in memory.

I have no idea what this means. Scenes don’t have “procedures”.

…old school Pascal programmer here… With ‘Procedure’ I mean script :wink:

Just found out that aside from the old level, also lists are kept in memory. This is real odd and I’m starting to wonder if this is not some sort of bug (still using Unity 4.6.3.f1)? I also tried to add an OnApplicationQuit() method/event to the MainGame, but that’s not working either. This means that at this moment I have to clear my levels completely before even starting to build a new one (which is freshly called when starting MainGame).

OnApplicationQuit() will not work in the editor. I think we’re confusing scripts, objects and scenes here. At first you stated that MainGame was a scene and now you say it’s a script.

I think we would need to see a bit of code in order to better assist you. For example your MainGame script and your level loading code.

OnApplicationQuit() is not working between scenes either. I have given it a try in a compiled EXE but yet the list stays in memory between the scenes :frowning:

MainGame is a scene and there’s an object with a linked script attached to it called MainGameHandler (my bad in the previous message). To the MainGameHandler I have added that OnApplicationQuit() when exiting to an other scene and that’s not working.

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

using PlayerPrefs = PreviewLabs.PlayerPrefs;

public class myBubbleList:IEquatable<myBubbleList>
{
   public int myFoundX { get; set; }
   public int myFoundY { get; set; }
   public int myColor { get; set; }
  
   public myBubbleList(int myX, int myY, int myC)
   {
     myFoundX=myX;
     myFoundY=myY;
     myColor=myC;
   }

   public override bool Equals(object obj)
   {
     if (obj==null) return false;
     myBubbleList objAsPart = obj as myBubbleList;
     if (objAsPart==null) return false;
     else return Equals (objAsPart);
   }
   public override int GetHashCode()
   {
     return myFoundX;
   }
   public bool Equals(myBubbleList other)
   {
     if(other==null) return false;
     return (this.myFoundX.Equals(other.myFoundX) && this.myFoundY.Equals(other.myFoundY)); // Return both X amd Y coordinates
   }

   public int CompareTo(myBubbleList other)
   {
     if(other==null)
     {
       return 1;
     }
     return myFoundX - other.myFoundX;
   }
}

public class MainGameHandler : MonoBehaviour
{

   // List of possible Bubbles to clear
   List<myBubbleList> MyBubbleList = new List<myBubbleList>();
   static private List<myBubbleList> specialBubbleList = new List<myBubbleList>();
   static public List<myBubbleList> SpecialBubbleList
   {
     get
     {
       return specialBubbleList;
     }
     set
     {
       specialBubbleList=value;
     }
   }

   // Initialize the game
   void Awake()
   {
     // Get the ACTUAL screen resolution
     MyScreenWidth=(float)Screen.width;
     MyScreenHeight=(float)Screen.height;

     // Initialize the current level
     PlayerPrefs.EnableEncryption(true); // Use encryption for PlayerPrefs()
     #if UNITY_EDITOR
     PlayerPrefs.EnableEncryption(false); // DO NOT use encryption for PlayerPrefs() in the editor!!!
     #endif
     MyLevel=PlayerPrefs.GetInt("Level");
     MyDifficulty=PlayerPrefs.GetInt("Difficulty");
     ScoreText.MyScore=PlayerPrefs.GetInt("Score");

     #if UNITY_ANDROID
     Screen.sleepTimeout = (int)SleepTimeout.NeverSleep;
     #endif
     Application.runInBackground = true;

     GamePaused=false;
     GameOver=false;
     gameOverMsg=false;
     LevelHandler.CreateLevel();
   }
  
   // Use this for initialization
   void Start ()
   {
     CreateBubbleGrid();
   }
  
   // Update is called once per frame
   void Update ()
   {
     if(!GamePaused)
     {
       if(Input.GetKeyDown (KeyCode.Space) && !GameOver)
       {
         GamePaused=true;
       }
       if(GameOver)
       {
         EndOfLevel();
       }
     }
     else
     {
       if(Input.GetKeyDown (KeyCode.Space))
       {
         GamePaused=false;
       }
     }
     if(Input.GetKeyDown (KeyCode.Escape) && !GameOver)
     {
       if(!GamePaused)
       {
         // Change into YES / NO selection later on
         Application.LoadLevel ("StartMenu"); // EXIT to main menu
       }
       else
       {
         GamePaused=false;
       }
     }
   }

   void EndOfLevel()
   {
     if(Input.GetKeyDown(KeyCode.Escape))
     {

       // Game Over - Figure out WHY it happened
       if (MyTimeLeft<=0)
       {
         // Out of time
         Application.LoadLevel("StartMenu");
       }
       if (MyColor2Kill<=0)
       {
         // Level completed
         PlayerPrefs.SetInt("Score",ScoreText.MyScore);
         MyLevel++;
         PlayerPrefs.SetInt("Level",MyLevel);
         PlayerPrefs.SetInt("MaxLevel",MyLevel);
         PlayerPrefs.Flush ();
        
         if(LevelData.LevelMovie(MyLevel)) Application.LoadLevel("Movies");
         else Application.LoadLevel("MainGame");
       }
     }
   }
}

Quite a lot there and I removed all things not really necessary. The CreateBubbleGrid() method in Start() is only created the actual level. The LevelHandler.CreateLevel() actually builds up the data to be used in CreateBubbleGrid() and creates a small list stored in SpecialBubbleList which is pretty straight forward:

     MainGameHandler.SpecialBubbleList.Add(new myBubbleList(9,14,32));
     MainGameHandler.SpecialBubbleList.Add(new myBubbleList(10,14,32));
     MainGameHandler.SpecialBubbleList.Add(new myBubbleList(8,16,32));
     MainGameHandler.SpecialBubbleList.Add(new myBubbleList(11,16,32));
     MainGameHandler.SpecialBubbleList.Add(new myBubbleList(9,18,32));
     MainGameHandler.SpecialBubbleList.Add(new myBubbleList(10,18,32));

SpecialBubbleList is static That’s why it isn’t cleaned up.

OnApplicationQuit is called when the program is exited - not between level loads. You’ll want OnLevelWasLoaded for that.

Hmm… That’s a problem. I need to make it static for other scripts to call and use it (like LevelHandler.CreateLevel()). Means I have to clean up all the static variables (including the list) at the load of the scene before initializing the whole thing again.

I’ll keep that one in mind for other (non static) things in the future. Thanks!

Not necessarily - the other scripts just need to find the instance of MainGameHandler.

Perhaps you should come up with a wrapper class to reduce the number of static variables. That way you only need to create a new instance to get rid of all the old stuff.

I don’t get it. Well, I do get the 2 above comments about the static LIST being held in memory and I should make an instance of MainGameHandler. But the below variable is static as well and NOT held in memory between scenes:

  // Number of KEYs owned
   static private int myOwnedKeys;
   static public int MyOwnedKeys
   {
     get
     {
       return myOwnedKeys;
     }
     set
     {
       myOwnedKeys=value;
     }
   }

It should be held in memory as well because it’s a static one but yet it is not held. And I made sure I didn’t yet clean it up when starting MainGameHandler

“Held in memory” is sort of a misnomer here because this example uses a primitive type. An integer always has some value associated with it (0 by default). Your previous example was a reference type (List) containing, essentially, pointers to other reference types. Managed reference types are only garbage collected when explicitly set to null or when nothing is referencing them anymore. Your static list is always keeping a reference to them so they’re never GC’d.

To further complicate this example - properties backed by primitive types return a copy of the backing field, not the backing field itself.

Now if you’re saying you set that property to some value other than 0 and loading a new level causes the value to be changed back to 0 then that’s something I’d not have expected to happen.