1
\$\begingroup\$

My Game Manager object has an Inventory script attached to it, which has "Don'tDestroyOnLoad(gameObject)" in its Awake function. During gameplay, items are pushed into a public List in this script. This is done successfully. The trouble comes when changing scenes, where either script instance is being overwritten or the object that was pushed into the list has been destroyed. How can I prevent this in a way that will scale well?

I am linking to a video you can watch of this problem happening in real time:

https://youtu.be/CLzDJg15VTk

This is the Inventory script, which is attached to my GameManager:

// Inventory.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Inventory : MonoBehaviour
{
    private static Inventory instance = null; // static (class level) variable
    public static Inventory Instance { get { return instance; } } // static getter (only accessing allowed)

    public List<GameObject> itemList;

    private void Awake()
    {
        // if instance is not yet set, set it and make it persistent between scenes
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            // if instance is already set and this is not the same object, destroy it
            if (this != instance) { Destroy(gameObject); }
        }
    }

    // debug only
    public void Debug_PrintItemInventory()
    {
        foreach(GameObject obj in itemList)
        {
            Debug.Log(obj.GetComponent<Item>().itemName);
        }
    }

    public void AddItem(GameObject item)
    {
        itemList.Add(item);
        Debug_PrintItemInventory(); // debug only
    }
}

This is the script that pushes an item into my character's inventory ("gm.GetComponent

// ItemBox.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemBox : MonoBehaviour
{
    bool disabled = false;
    private Animator anim;
    [SerializeField]
    private GameObject[] items = new GameObject[3];
    [SerializeField] private GameObject gm;

    void Start()
    {
        anim = GetComponent<Animator>();
        if (!disabled)
        {
            anim.Play("ItemBoxIdleState");
        }
    }

    void OnTriggerEnter(Collider other)
    {
        if (!disabled && other.gameObject.tag == "Player")
        {
            anim.Play("ItemBoxGetItemState");
            int randInt = Random.Range(0, 3);
            GameObject itemObj = Instantiate(items[randInt].GetComponent<Item>().getPrefab, transform.position + new Vector3(0, 0.375f, 0), Camera.main.transform.rotation);
            gm.GetComponent<Inventory>().AddItem(Instantiate(items[randInt]));
            Destroy(itemObj, 1.6f);
            disabled = true;
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ 1. Do you have another Inventory component set in the scene you're loading ? 2. Can you see your GameObject in the hierarchy once the new scene is loaded ? \$\endgroup\$ – lvictorino Oct 24 at 8:53
  • \$\begingroup\$ 1. No; 2. Which GameObject? As in the video I linked to, we can see the "Game Manager" GameObject, and the script attached to it, but not the item GameObject that should have come with it. We can see that when instantiate the item into the itemList in the Inventory script, that item appears outside the DoNotDestroy scope in the Hierarchy, which I did not expect. In the main scene, it is in the script's itemList, as it should be. So it is not clear to me if the GameObject and the script are being completely overwritten or if the item just isn't coming along for the ride \$\endgroup\$ – hatinacat2000 Oct 24 at 8:59
  • \$\begingroup\$ I also tried eliminating the "else" case in the Awake() method but it did not help \$\endgroup\$ – hatinacat2000 Oct 24 at 9:01
4
\$\begingroup\$

gm.GetComponent<Inventory>().AddItem(Instantiate(items[randInt]));

On this line you instantiate a new GameObject on your scene, which happens before loading the new scene. You put a reference of that new object in the list of the Inventory (which is set as persistent during loadings).

When you ask for a scene loading, all the content of your current scene is destroyed except game objects that are marked as persistent (via DontDestroyOnLoad()). So your Inventory object is kept, its content too, but the item you created is destroyed... and so the reference to it in the inventory list too. That explains why the Inventory still has one item in its list, but it's null.

The easy trick here, would be to set the Inventory (that is not destroyed on load) as the parent of the created item. It'll make it persistent, so the reference will be kept accross loadings.

\$\endgroup\$
  • 1
    \$\begingroup\$ @Ivictorino, I did exactly this and now it all seems to work. It even shows up in my BattleUI, which I did not go over. Thank you! \$\endgroup\$ – hatinacat2000 Oct 24 at 9:21
  • 1
    \$\begingroup\$ ;) I'm glad it worked. Cheers! \$\endgroup\$ – lvictorino Oct 24 at 9:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.