How do I properly destroy the instance of singleton and dispose of it's fields?

I have an instance of a LevelManager class, that I’ve made a singleton. This class stores data about the objects the player has to interact with in different scenes.
It looks like this:

public class LevelManager : MonoBehaviour
{
    public static LevelManager levelManagerInstance;
    List<sometype> objectsList = new List<sometype> ();

    private void Awake()
    {
        if (levelManagerInstance != null)
        {
            Destroy(gameObject);
            return;
        }
        levelManagerInstance = this;
        DontDestroyOnLoad(gameObject);
   }
   //some other code
}

But I wan’t to clear that data and destroy that instance whenever the player goes into the main menu, so that when the player starts the game again - all the items are reset. How do I do that?
I’ve tried removing the game object from the DontDestroyOnLoad list. I’ve tried removing both the gameObject and the levelManagerInstance with Destroy(), which removes them from the hierarchy, but whenever I load the level the objects that the player previously interacted with are not present (but they should be).

Please help :slight_smile:
Thanks

You should be able to just do this.

public void ClearDataAndDestroyInstance()
{
    objectsList.Clear();
    levelManagerInstance = null;
    Destroy(gameObject);
}

So I see… There is no way to automatically clear all that data whenever I destroy that instance? Only to manually clear each variable and then simply destroy the gameobject?

You don’t need to clear every variable.

Once you null the static instance and destroy the game object, everything else gets cleaned up by the garbage collector.

Note: when you destroy the singleton and recreate it you are very likely to fall into a trap. Not only is your singleton no longer a singleton (as in: the only reference) you will also run into issues with other scripts calling into the singleton, not expecting that it is either a new instance or has changed internal state.

This shouldn‘t be a singleton but just a regular component in the scene that other components get a reference to.
It also isn‘t a manager. It‘s your LevelObjects or something more concrete like this.

You could introduce a boolean field to the singleton class, which could be used to control whether a loaded instance should replace an existing instance or not - and then only set this true for the instance in the main menu scene:

public sealedclass LevelManager : MonoBehaviour
{
    [SerializeField] bool replaceExistingInstance;
    List<sometype> objectsList = new List<sometype>();
 
    public static LevelManager Instance { get; private set; }

    void Awake()
    {
        if(Instance != null)
        {
            if(!replaceExistingInstance)
            {
                Destroy(gameObject);
                return;             
            }

            Destroy(Instance.gameObject);
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);
   }
}

Or you could merely reset the state of the existing instance, instead of creating a new one:

public sealed class LevelManager : MonoBehaviour
{
    sealed class MutableState
    {
        public readonly List<sometype> objectsList = new();
    }
 
    [SerializeField] private int resetStateWhenSceneByIndexLoaded;
 
    MutableState state = new();

    public static LevelManager Instance { get; private set; }
    void Awake()
    {
        if(Instance != null)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);
        SceneManager.sceneLoaded += OnSceneLoaded;
    }
 
    void OnDestroy() => SceneManager.sceneLoaded -= OnSceneLoaded;
 
    void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        if(scene.buildIndex == resetStateWhenSceneByIndexLoaded)
        {
            ResetState();
        }
     
        void ResetState() => state = new();
    }
}

This could be more reliable, because if any client should cache a reference to the singleton instance during initialization, then that instance would just be reset instead of becoming “null” (destroyed).

If you do singleton-y stuff in Awake() you probably have made a defective singleton.

If you ever contemplate dragging a singleton into a scene in Unity, you have almost certainly made a defective singleton.

Instead, try this approach:

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

These are pure-code solutions, DO NOT put anything into any scene, just access it via .Instance

Alternately you could start one up with a RuntimeInitializeOnLoad attribute.

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

OR just make a custom ScriptableObject that has the shared fields you want for the duration of many scenes, and drag references to that one ScriptableObject instance into everything that needs it. It scales up to a certain point.

If you really insist on a barebones C# singleton, here’s a highlander (there can only be one):

And finally there’s always just a simple “static locator” pattern you can use on MonoBehaviour-derived classes, just to give global access to them during their lifecycle.

WARNING: this does NOT control their uniqueness.

WARNING: this does NOT control their lifecycle.

public static MyClass Instance { get; private set; }

void OnEnable()
{
  Instance = this;
}
void OnDisable()
{
  Instance = null;     // keep everybody honest when we're not around
}

Anyone can get at it via MyClass.Instance., but only while it exists.

Well, it seems like it doesn’t work like this.
I have this code in my MainMenu script, the LevelManager game object is destroyed, the Instance value is nulled. I even specifically call garbage collector. But whenever I start the new game and create a new LevelManager - it references the values that were in the “destroyed” LevelManager (like different bools and lists).

    private void Start()
    {
        if (LevelManager.levelManagerInstance != null)
        {
            Destroy(LevelManager.levelManagerInstance.gameObject);
            LevelManager.levelManagerInstance = null;
            System.GC.Collect();
        }
    }

This has to be a singleton (or some other pattern with similar behaviour), since it tracks a list of all the objects player has to interact with on two different scenes (indoor and outdoor). If it’s going to be just a simple object in scene, whenever the player will go inside, the quest finisher will have no knowledge of what was done outside. And I don’t want to make 2 or more copies of a list, if the player goes inside and outside and inside and so on. There always has to be just one copy of that list.

Ok, maybe I don’t need to use Singleton then, but some other pattern?
The idea was to make simple scriptable object like this:

    public List<sometype> requiredIteractions;
    public enum locations { exterior, interior }
    [System.Serializable]
    public class sometype
    {
        public string objectKey;
        public locations location;
    }

So that I could create different scriptable objects for different levels and define what has to be done there.
Then each level (that will usually consist of two scenes, outside and inside) will have a singleton LevelManager inside the scene, that will have a reference to a specific scriptable object via inspector. The LevelManager will get the list from ScriptableObject, make a new copy of that list and keep track of everything that player has to do in those two scenes.
And then whenever the player finishes the level or goes into main menu - the game would destroy this LevelManager, and (if needed) create a new one with a reference to another ScriptableObject.

If the singleton pattern is not the best approach here, how should I properly do it?
Thanks.

A Singleton construct is ONLY to live beyond the length of a single scene, such as the lifetime of your game.

You actually can also use a ScriptableObject AS your LevelManager, as its lifetime is not tied to loading scenes in the way that MonoBehaviours are.

Just think of ScriptableObjects as “bags of data.”

ScriptableObject usage in RPGs:

https://discussions.unity.com/t/798767/2

https://discussions.unity.com/t/803356/2

Usage as a shared common data container:

https://www.youtube.com/watch?v=PVOVIxNxxeQ

Welp. Turns out you were right. I just needed to unsubscribe from all the events I was listening to before unloading the scene. And a soon as I did that - the game started behaving the way I expected it to behave.