Search code examples
c#unity-game-enginesaveloadinventory

Save and load inventory


I've already created the inventory and everything works fine. Now I should implement the saving and loading of the inventory (on file). However, I find myself stuck on how to proceed. I was thinking of creating an Inventory Data script to get serializable data and then save it. I'm not using scriptable object. Would you have any advice for me? Below is the inventory code.

 public class Inventory2 : MonoBehaviour
 {
     public bool inventoryEnabled;
     public GameObject inventory, slotHolder;
     private Transform[] slot;
     public int level;

     void Start()
     {
         level = SceneManager.GetActiveScene().buildIndex;
         GetAllSlots();        
     }   

     void Update()
     {
         if (Input.GetAxis("Inventory") != 0)
         {
             inventoryEnabled = !inventoryEnabled;
         }

         if(inventoryEnabled)
         {
             inventory.SetActive(true);
         }
         else
         {
             inventory.SetActive(false);
         }
     }

     public void OnTriggerEnter(Collider other)
     {
         if (other.tag == "Clues")
         {            
             AddClue(other.GetComponent<Clue2>());            
         }
     }
     public void AddClue(Clue2 clue)
     {
         Text description = clue.GetComponent<Text>();

         for (int i = 0; i < 2; i++)
         {
             if(slot[i].GetComponent<Slot2>().empty == true && clue.pickedUp == false)
             {
                 slot[i].GetComponent<Slot2>().clue = clue;
                 slot[i].GetComponent<Slot2>().descriptionFinal = description;
                 slot[i].GetComponent<Slot2>().empty = false;
                 clue.GetComponent<Clue2>().pickedUp = true;
             }
         }
     }

     public void GetAllSlots()
     {
         slot = new Transform[Clue2.objects];

         for(int i = 0; i < Clue2.objects; i++)
         {
             slot[i] = slotHolder.transform.GetChild(i);
             slot[i].GetComponent<Slot2>().empty = true;
         }
     }
 }
 public class Slot2 : MonoBehaviour
 {
     public Clue2 clue;
     public bool empty;
     public Text descriptionFirst, descriptionFinal;

     void Awake()
     {

     }

     void Update()
     {
         if (clue)
         {
             this.GetComponentInChildren<Text>().text = descriptionFinal.text;
         }
         else
         {
             this.GetComponentInChildren<Text>().text = descriptionFirst.text;
             empty = true;
         }
     }
 }
 public class Clue2 : MonoBehaviour
 {
     public static int objects = 0;
     public static int objectsCollected = 0;
     public Text description;
     public bool pickedUp;

     public GameObject cluePopUpPanel, canvasCluesPanel;
     public Canvas canvasHUD;
     public static bool activeClue = false;

     void Awake()
     {
         objects++;
     }

     void Update()
     {
         if (canvasCluesPanel.gameObject.activeSelf == true && Input.GetAxis("PickUp") != 0)
         {
             activeClue = true;
             cluePopUpPanel.gameObject.GetComponent<UnityEngine.UI.Text>().text = description.text;
         }
     }

     private void OnTriggerEnter(Collider other)
     {
         if (other.tag == "Player")
         {
             if (canvasCluesPanel.gameObject.activeSelf == false)
             {
                 canvasCluesPanel.gameObject.SetActive(true);                
             }
         }
     }

     private void OnTriggerStay(Collider other)
     {
         if (other.tag == "Player" && activeClue == true)
         {
             cluePopUpPanel.gameObject.SetActive(true);
             cluePopUpPanel.GetComponentInChildren<Text>().text = this.GetComponent<Text>().text;
             canvasCluesPanel.gameObject.SetActive(false);
         }

         if (other.tag == "Player" && activeClue == false)
         {
             cluePopUpPanel.gameObject.SetActive(false);
             canvasCluesPanel.gameObject.SetActive(true);
         }        
     }

     private void OnTriggerExit(Collider other)
     {
         if (other.tag == "Player" && activeClue == true)
         {
             cluePopUpPanel.gameObject.SetActive(false);
             canvasCluesPanel.gameObject.SetActive(false);
             activeClue = false;
             if(objectsCollected < objects)
             {
                 objectsCollected++;
             }
         }
         else
         {
             cluePopUpPanel.gameObject.SetActive(false);
             canvasCluesPanel.gameObject.SetActive(false);
             activeClue = false;
         }

         canvasHUD.gameObject.SetActive(true);
     }
 }

Solution

  • A good tutorial can be found here.

    1 - Create the data type

    As a good practice Store the data you want to save in a class. Create the SlotData, like so. So try converting them like so. This class must have the attribute System.Serializable.

    One example could be this

    [System.Serializable]
    public class SlotData
    {
        public bool containsItem = false;
        public string Description;
        //other possible elements
        public int amount; 
    }
    

    The InventoryData will be just an array of SlotData.

    [System.Serializable]
    public class InventoryData
    {
        public SlotData[] inventorySlots;
    }
    

    2 - Setup the data before saving

    Somewhere in your code you update the Inventory. You must setup this data, probably from the monobehaviour. Basically you add all the info you want in your class.

    public void PrepareToSave()
    {
        //setup the inventory
        var yourInventory = new InventoryData();
        //fill all the slots, you must also calculate their amount
        yourInventory.inventorySlots = new SlotData[CalculateYourAmount];
        //fill all the slots here
        for (int   i= 0; i  < CalculateYourAmount;  i++)
        {   
            //fill all the slots
            yourInventory.inventorySlots[i] = CreateTheSlot();
        }
    
        //this go to the next step
        SaveYourInventory(yourInventory);
    }
    

    3 - Save the inventory

    As you asked you may use BinaryFormatter and FileStream.

    private void SaveYourInventory(InventoryData yourInventory)
    {
        var             savePath = Application.persistentDataPath + "/inventory.dat";
        BinaryFormatter bf       = new BinaryFormatter();
        FileStream      file     = File.Create(savePath);
        bf.Serialize(file, yourInventory);
        file.Close();
    }
    

    4 - Load the inventory

    Then you can just load the inventory like this.

    public void LoadYourInventory()
    {
        var savePath = Application.persistentDataPath + "/inventory.dat";
        if (File.Exists(savePath))
        {
            BinaryFormatter bf   = new BinaryFormatter();
            FileStream      file = File.Open(savePath, FileMode.Open);
            InventoryData yourInventory = (InventoryData) bf.Deserialize(file);
            file.Close();
    
            //do what you want with the inventory
            ...
        }
    }
    

    You can also find a Save Manager system here.

    And if you want to go more advanced you can use this optimized formatter (or save it for the future if you get back to the save/load business).