How can i persist the state of a scene when moving between scenes

like stuff collected, bosses defeated, doors opened and so on. i want them to not go back to their original state after returning to the scene again.

i couldn’t find a solution on the internet, please help me :cry:

Hi,

I remember this question, and I thought I already answered it. Maybe on Udemy?

You could use the “singleton” object to store your data. Of course, you’ll have to define what “stuff collected” (an inventory?), “doors opened” (just the number, keeping track of doors?) and so on means. On Youtube, you can find quite a few tutorials. Also please feel free to ask our helpful community of students over on our Discord chat server.

1 Like

sorry yeah, but you said that the udemy course was beginner course and i should ask the gamedev community.

and sorry but you kinda misunderstood my question, what i’m asking is how to make the stuff i collected to not respawn when i reload the scene. i have an inventory system and it works great but if i reload the scene everything respawns.
thanks!

No, @Nina is right. You could keep the state of things in a singleton object that persists. When the scene loads, all the things you are worried about will load in their default state. They will then need to check with this object what their state should be. If it’s a door, the door will check if it should be open or closed. If it’s a coin, it should check if it was picked up and destroy itself if it was. And every time one of these things’ state changes (door goes from open to closed) they will have to store this state in the singleton object

1 Like

If the problem is limited to the same level, I vaguely remember that we had a simple solution for collectibles such as coins. Maybe in one of the previous versions of the 2D course? We created a new game object and parented it to the “singleton” game object. And to this game object, we parented our coins. The coins persisted. We only destroyed them when the player collected them or when we loaded a new level, which was not the same.

You could apply this idea to a lot of other things in your scene. As long as a door is a child of a “singleton”, the door will persist and keep its state, even if you reload the scene.

If you need a more complex and maybe more flexible solution, @bixarrio’s idea where you store the states instead of the game objects object, might be better.

This really depends on what you have and need in your game.

1 Like

It’s in TileVania. The objects live under the persisted object and when the level reloads - because the player died, for instance - they are persisted. But when a new level loads, that object is destroyed. On purpose. This is good if the player will never return to that level. If the player could come back to the level you’ll need to keep the state beyond the current level and then have some mechanism to store and retrieve identifiable state, and for the affected objects to store the state when it changes and retrieve it when they load.

1 Like

If the player could come back to the level you’ll need to keep the state beyond the current level and then have some mechanism to store and retrieve identifiable state, and for the affected objects to store the state when it changes and retrieve it when they load.

Ah yes, you are right. Maybe one could modify the current course solution a bit by not destroying the persistent game object. Instead, one could create multiple child game objects, one for each level. Based on the current level (SceneManager.sceneLoaded), one could disable/enable the corresponding child game objects. This is similar to Gary’s solution for the Quiz Canvas and Win Canvas in the quiz master game where he enabled and disables screens when needed.


If storing the values in a C# object is necessary, I’d probably opt for a pure C# object instead of the Unity “singleton”. Then one would never have to worry about about Unity, and the data would always be available while the game is running.

really sorry, but i couldn’t for the life of me do it, because i have multiple of the same gameobject in my game and couldn’t differentiate each object for the singleton. i tried to serialize bools and IDs for each object, but couldn’t find a way to wire them to the singleton script.
i feel like there’s a very simple solution but for some reason i can’t figure it out.

Well, in that case, proceed with the course, learn more, become more familiar with programming and Unity. And once you are more experienced than today, go back to your project to realise your idea. It is absolutely normal that you cannot realise all your ideas immediately. Sometimes, an idea sounds simple but the solution requires a lot of research and is more complex than expected.

1 Like

Thank you, i really appreciate the advice. and yeah after giving it a few days and not stressing over it i found a way and it thankfully worked :slight_smile:
i just hope it won’t affect performance.
here’s the script can you please tell me if there’s something wrong with it?

it’s a script for a crystal and i named each instance with a unique name and created a bool similar to that name in a singleton called "Bools’’ to hold the data. and made it check if it has already been collected at start, and if a player triggered it it changes its state to being collected by changing the bool to false.

public class CrystalPickup : MonoBehaviour

{

bool collected = false;

Bools bools;

void Start()

{

    bools = FindObjectOfType<Bools>();

    if(gameObject.name == "A1")

    {

        if(!bools.CrystalA1){Destroy(gameObject);}

    }

    if(gameObject.name == "A2")

    {

        if(!bools.CrystalA2){Destroy(gameObject);}

    }

    if(gameObject.name == "B1")

    {

        if(!bools.CrystalB1){Destroy(gameObject);}

    }

    if(gameObject.name == "B1")

    {

        if(!bools.CrystalB2){Destroy(gameObject);}

    }

}

private void OnTriggerEnter2D(Collider2D other)

{

    if(other.tag == "Player" && !collected)

    {

    if(gameObject.name == "A1")

    {

        bools.CrystalA1 = false;

    }

    if(gameObject.name == "A2")

    {

        bools.CrystalA2 = false;

    }

    if(gameObject.name == "B1")

    {

        bools.CrystalB1 = false;

    }

    if(gameObject.name == "B1")

    {

        bools.CrystalB1 = false;

    }

        Destroy(gameObject);

        collected = true;    

    }

}

}

public class Bools : MonoBehaviour

{

//Crsystals

public bool CrystalA1 = true;

public bool CrystalA2 = true;

public bool CrystalB1 = true;

public bool CrystalB2 = true;

}
or is it better to use static variables instead of creating another script only to hold the data? (sorry i’m not really familiar with them so i just wanted to ask a professional to make sure)
thanks!!!

This will work but it is really not scalable. You really don’t want to be creating variables and ‘if’ statements for every possible pickup in the game (and later some other things you may want to persist). It also makes it impossible to have any pickups spawned by code. You have to place them all by hand and then create the variables yourself.

What you could do is to give the pickup a unique identifier, and then store the state in a dictionary in a singleton class somewhere.

Let’s start by creating a singleton class that can hold the state

public class PickupState : MonoBehaviour
{
    public static PickupState Instance { get; private set; }

    private Dictionary<string, bool> stateDict = new Dictionary<string, bool>();

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}

This is just a basic singleton with a dictionary to hold the state. Next, we want a method in here that will allow us to get the state of a pickup from the dictionary. This goes into this same singleton

public bool GetPickupState(string pickupID)
{
    if (stateDict.ContainsKey(pickupID))
        return stateDict[pickupID];
    return false;
}

With this method we can ask the PickupState singleton for a value. If the dictionary does not have state for the pickup, it will return false (not picked up).

Next, we need a way to store the state. This is quite simple;

public void SetPickupState(string pickupID, bool state)
{
    stateDict[pickupName] = state;
}

That’s it for the state singleton. What we need now is for your pickups to be able to use it. I would use a base class that implements the state stuff but I’m afraid that might complicate stuff. What you need is two things;

  • A unique ID for each pickup, and
  • A way to get the state from the singleton

We can expose a unique ID field in the inspector for you to fill in.

We define a SerializedField to hold this value. I am adding an OnValidate here to fill it in with a unique ‘id’, but be aware that this will only run in editor. It will not create a unique id at runtime

public void Pickup : MonoBehaviour
{
    [SerializeField] string uniqueID;

    private void OnValidate()
    {
        if (string.IsNullOrWhitespace(uniqueID))
        {
            uniqueID = GUID.Generate().ToString();
        }
    }
}

The OnValidate() will generate and serialise a relatively unique value (that looks like 0656c7ea2d575d74fbf4118141e910ef) for your object, but only if it doesn’t already have one - which is the case as soon as you create the object, so it will immediately have an ID when you place the object in the scene. It doesn’t really matter what it looks like, as long as it’s unique. If you want, you can change it to whatever you would like, provided it’s unique. This just removes the need to fill in IDs every time you place a pickup.

Next, we just need to retrieve and set the state, much like you did already

private void Start()
{
    if (PickupState.Instance.GetPickupState(uniqueID))
    {
        Destroy(gameObject);
    }
}

private void OnTriggerEnter2D(Collider2D other)
{
    if (!other.CompareTag("Player"))
    {
        return;
    }

    PickupState.Instance.SetPickupState(uniqueID, true);
    Destroy(gameObject);
}

That’s pretty much it. Make sure the PickupState is on a game object in the hierarchy on the very first level (or before even) and it should do what you want.


If you want a base class (as mentioned earlier) it would be something like this

public abstract class Pickup : MonoBehaviour
{
    [SerializeField] protected string uniqueID;

    protected virtual void OnValidate()
    {
        if (string.IsnullOrwhitespace(uniqueID))
        {
            uniqueID = GUID.Generate().ToString();
        }
    }

    protected virtual void Start()
    {
        if (PickupState.Instance.GetPickupState(uniqueID))
        {
            Destroy(gameObject);
        }
    }

    protected virtual void Pickup()
    {
        PickupState.instance.SetPickupState(uniqueID, true);
        Destroy(gameObject);
    }
}

and then you can use it like this (the required stuff happens in the base class)

public class CrystalPickup : Pickup
{
    private void OnTriggerEnter2D(Collider2D other)
    {
        if (!other.CompareTag("Player"))
        {
            return;
        }
        Pickup();
    }
}
1 Like

THANK YOU SO MUCH!! i really can’t thank you enough for how much you’ve helped me.
i was looking for something similar to the dictionary and didn’t know it existed, thank you again! :slight_smile:

Once again, @bixarrio was faster than I, so I just wanted to add “good job, xADNAN, and thank you for your patience and for sticking with the problem”. I’m sure you learnt a lot by solving this problem. :slight_smile:

1 Like

YES, thank you really both of you and @bixarrio have helped me a lot

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms