How would I go about saving my Inventory Status?

Discussion and help for Easy Save 3
Post Reply
saambell1996
Posts: 5
Joined: Sat Jun 29, 2019 2:11 pm

How would I go about saving my Inventory Status?

Post by saambell1996 »

Image

https://imgur.com/a/HOu31sT

Hey Joel, not sure if my gmail message went through so going to post on here! In the process of making a Castaway Sim that will see the player explore an island and unlock new segments and NPC's over time! They will also be able to craft items in the future that will help make stat replishinment on the island a little bit easier! So far I have managed to get my Slider Value (stats) data saving pretty flawlessly by saving the Slider values as floats and then loading them back in when the load button is hit, but now I've hit a tougher problem it would seem.
I have recently been following Brackeys RPG tutorial as that has a guide on how to set up a Scriptable Objects based Inventory which I had used for this project, my only question now is how to save the status of that Inventory using Easy Save 3.
This is some code from my Inventory scripts that hopefully show you how it works in general!

Code: Select all

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class Inventory : MonoBehaviour {

    #region Singleton

    public GameObject player;
    public static Inventory instance;


    //Tooltip  Variables
    [SerializeField]
    private GameObject tooltip;

    public TextMeshProUGUI tooltipTitle;
    public Image tooltipImage;
    

    private void Awake()
    {
      
        instance = this;
        tooltipTitle.GetComponent<TextMeshProUGUI>();
        tooltipImage.GetComponent<Image>();
    
    }

    #endregion

    public delegate void OnItemChanged();

    public OnItemChanged onItemChangedCallBack;

    public int space = 20;

    public List<Item> items = new List<Item>();

    public void Add (Item item)
    {
      //  if (!item.isKeyItem)
      //  {
            if (items.Count >= space)
            {
                Debug.Log("Now enough space in Inventory!");
                return;
            }
            items.Add(item);

        if (onItemChangedCallBack != null)
            {
            onItemChangedCallBack.Invoke();
            }
      //  }
        return;
       
    }
    public void Remove (Item item)
    {
      
            items.Remove(item);
        if (onItemChangedCallBack != null)
        {
            onItemChangedCallBack.Invoke();
        }
    }

    public void ShowToolTip(IDescribable ToolTipitem)
    {
        tooltip.SetActive(true);
        tooltipTitle.text = ToolTipitem.GetTitle();
        tooltipImage.sprite = ToolTipitem.GetSprite();
      
    }

    public void HideToolTip()
    {
        tooltip.SetActive(false);
    }
}

Code: Select all


using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class InventoryUI : MonoBehaviour
{
    
    public Transform itemsParent;
    public GameObject inventoryUI;
    Inventory inventory;

   
    // Start is called before the first frame update
    void Start()
    {
        inventory = Inventory.instance;
        inventory.onItemChangedCallBack += UpdateUI;
      
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetButtonDown("Inventory"))
        {
            inventoryUI.SetActive(!inventoryUI.activeSelf);
            UpdateUI();
        }
        
    }

    void UpdateUI()
    {
        InventorySlot[] slots = itemsParent.GetComponentsInChildren<InventorySlot>();
        for (int i = 0; i < slots.Length; i++)
        {
            if (i < inventory.items.Count)
            {

                slots[i].AddItem(inventory.items[i]);
               
               
               
            }
            else
            {
                slots[i].ClearSlot();
                
            }
        }
    }
}

Code: Select all

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;
using TMPro;

public class InventorySlot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
 
    
    public TextMeshProUGUI itemText;
    public Image icon;
    Item item;
    public Button removeButton;
    public GameObject dropItemModal;
    public Animator spriteAnim;
    public Button yesButton;
   

    public void Start()
    {
       

    }

    public void AddItem (Item newItem)
    {
        
       
        spriteAnim.Play("SpriteBounce");
        item = newItem;
        icon.sprite = item.icon;
        icon.enabled = true;
        removeButton.interactable = true;
     
      

    }

    public void ClearSlot()
    {
        item = null;
        icon.sprite = null;
        icon.enabled = false;
        removeButton.interactable = false;
    }

    public void onRemoveButton()
    {

         Inventory.instance.Remove(item);

        #region   
        /*   dropItemModal.SetActive(true);
        yesButton.onClick.AddListener(onYesClick);
           itemText.text = item.name;

                  if (item.isKeyItem != false)
                  {
                      yesButtonDisabled.interactable = false;
                      itemText.text = "You Can't Drop a Key Item!";
                  }
                  else
                  {
                      yesButtonDisabled.interactable = true;
                  }
                  */
        #endregion

    }
    #region
    /* public void onYesClick()
     {
       //  Inventory.instance.Remove(item);
         dropItemModal.SetActive(false);
     }

     public void onNoClick()
     {
         dropItemModal.SetActive(false);
     } */
    #endregion

    public void UseItem()
    {

        if (item != null)
        {
           
            item.Use();
        }
    }

  

    public void OnPointerEnter(PointerEventData eventData)
    {
        //Show Tooltip
        if (item != null)
        {
            Debug.Log(item.name + " " + "Is in this slot");
           Inventory.instance.ShowToolTip(item);
        }

        else
        {
            Debug.Log("Inventory Slot Empty");
        }
      
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        //Hide Tooltip
        Debug.Log("Exit");
        Inventory.instance.HideToolTip();
    }

  
}

The first screenshot also shows the hierachy of my project and how the inventory is layed out in general! Any help here would be greatly appreciated :)!
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: How would I go about saving my Inventory Status?

Post by Joel »

Hi Sam,

Your question is quite broad, but I will try to point you in the right direction. I'll focus on the inventory slots, but this should give you an idea of how to apply it to the other areas.

Generally the best way to do this would be to go to Window > Easy Save 3 > Types, search for your 'InventorySlot' type and select the fields you want to be saved from there. Note that by default fields of reference types are saved by reference (for example your itemText field). I'm assuming that you want to save the text of your itemText, so we'll save this separately.

Once you've added it in the Types pane, you could create a save script with a Save() and Load() method as follows. Note that you could call these in Start() and OnDestroy() or OnApplicationQuit(), or you could hook them up to a button.
public void Save()
{
    ES3.Save<InventorySlot>(this.name, this.GetComponent<InventorySlot>());
    // This saves the text. This assumes that your itemText has a field called text.
    ES3.Save<string>(this.name + "text", this.GetComponent<InventorySlot>().itemText.text);
}

public void Load()
{
    if(!ES3.KeyExists(this.name))
        return;
    ES3.LoadInto<InventorySlot>(this.name, this.GetComponent<InventorySlot>());
    // This saves the text. This assumes that your itemText has a field called text.
    this.GetComponent<InventorySlot>().itemText.text = ES3.Load<string>(this.name + "text");
}
Are any of the fields you are saving instantiated/created at runtime? If so, these will not be in the reference manager, so you may need to do something different for them.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
saambell1996
Posts: 5
Joined: Sat Jun 29, 2019 2:11 pm

Re: How would I go about saving my Inventory Status?

Post by saambell1996 »

Hey man, thanks for replying!

So right now my Save & load functions are both in one script and attatched to my Game Manager script which is just an empty object in the scene, how much would I need to change of the script you posted to get reference to my Inventory slot? would I just set up a public gameobject and get a reference to the script from there :)?

i'm not too sure where I should really be putting this script i guess, still very new to ES3 :)!


*Update*
okay so good news!
using this code

Code: Select all

    public void Save()
    {
       
        StatScripts statsRef = player.GetComponent<StatScripts>();
 
        

        Debug.Log("Saved Game");
        ES3.Save<float>("hungerValue", statsRef.currentHunger);
        ES3.Save<float>("restValue", statsRef.currentRest);
        ES3.Save<float>("socialValue", statsRef.currentSocial);
        ES3.Save<float>("hygineValue", statsRef.currentHygine);
        ES3.Save<float>("bathroomValue", statsRef.currentBathroom);
        ES3.Save<float>("comfortValue", statsRef.currentComfort);
        ES3.Save<InventorySlot>("inventoryKey", inventorySlot.GetComponent<InventorySlot>());
        ES3.Save<string>("inventoryText" + "text", inventorySlot.GetComponent<InventorySlot>().itemText.text);



    }

    public void Load()
    {
    
        StatScripts statsRef = player.GetComponent<StatScripts>();
        Debug.Log("Loaded Game");
        statsRef.currentHunger = ES3.Load<float>("hungerValue");
        statsRef.currentRest = ES3.Load<float>("restValue");
        statsRef.currentSocial = ES3.Load<float>("socialValue");
        statsRef.currentHygine = ES3.Load<float>("hygineValue");
        statsRef.currentBathroom = ES3.Load<float>("bathroomValue");
        statsRef.currentComfort = ES3.Load<float>("comfortValue");

        if (!ES3.KeyExists("inventoryKey"))
            return;
        ES3.LoadInto<InventorySlot>("inventoryKey", inventorySlot.GetComponent<InventorySlot>());
        inventorySlot.GetComponent<InventorySlot>().itemText.text = ES3.Load<string>("inventoryText" + "text");
     
       
     
      
    }
after saving i can now hover over my currently empty InventorySlot on the first panel and see my tooltip appear for the right item, in this case watermelon! can you spot any problems with my current code before i continue mate :)?
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: How would I go about saving my Inventory Status?

Post by Joel »

That's looking good to me at the moment, assuming I correctly understand how your project works :)

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
saambell1996
Posts: 5
Joined: Sat Jun 29, 2019 2:11 pm

Re: How would I go about saving my Inventory Status?

Post by saambell1996 »

Hey Joel, quick update, seem to be having a problem using EasySave3 to updated the remainded of my inventory slots!

Code: Select all

using System.Collections;
using UnityEngine.SceneManagement;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TMPro;

public class SimpleSave : MonoBehaviour
{

    public GameObject Manager;
    public GameObject player;
    public GameObject playerHolder;
    public GameObject inventorySlot;
    public GameObject inventoryIcon;

    public GameObject inventoryUI;
    public GameObject canvas;


  
    


    public void Save()
    {
       
        StatScripts statsRef = player.GetComponent<StatScripts>();
        MenuPause MP = canvas.GetComponent<MenuPause>();
        

        Debug.Log("Saved Game");
        ES3.Save<float>("hungerValue", statsRef.currentHunger);
        ES3.Save<float>("restValue", statsRef.currentRest);
        ES3.Save<float>("socialValue", statsRef.currentSocial);
        ES3.Save<float>("hygineValue", statsRef.currentHygine);
        ES3.Save<float>("bathroomValue", statsRef.currentBathroom);
        ES3.Save<float>("comfortValue", statsRef.currentComfort);
        ES3.Save<InventorySlot>("inventoryKey", inventorySlot.GetComponent<InventorySlot>());
        ES3.Save<List<Item>>("inventoryItem", GetComponent<Inventory>().items);


        ES3.Save<string>("inventoryText" + "text", inventorySlot.GetComponent<InventorySlot>().itemText.text);
        ES3.Save<Sprite>("inventoryIcon", inventoryIcon.GetComponentInParent<InventorySlot>().icon.sprite);

        if(inventorySlot.GetComponent<InventorySlot>().enabled == false)
        {
            Debug.Log("Inventory is disabled but still saved with UpdateUI");
        }

        MP.Resume();




    }

    public void Load()
    {
        InventoryUI IUI = inventoryUI.GetComponent<InventoryUI>();
        MenuPause MP = canvas.GetComponent<MenuPause>();

        //Stats Loading

        StatScripts statsRef = player.GetComponent<StatScripts>();
        Debug.Log("Loaded Game");
        statsRef.currentHunger = ES3.Load<float>("hungerValue");
        statsRef.currentRest = ES3.Load<float>("restValue");
        statsRef.currentSocial = ES3.Load<float>("socialValue");
        statsRef.currentHygine = ES3.Load<float>("hygineValue");
        statsRef.currentBathroom = ES3.Load<float>("bathroomValue");
        statsRef.currentComfort = ES3.Load<float>("comfortValue");


        //Inventory Loading
        if (!ES3.KeyExists("inventoryKey"))
            return;

        ES3.LoadInto<InventorySlot>("inventoryKey", inventorySlot.GetComponent<InventorySlot>());

        GetComponent<Inventory>().items = ES3.Load<List<Item>>("inventoryItem");
        IUI.UpdateUI();
        inventorySlot.GetComponent<InventorySlot>().itemText.text = ES3.Load<string>("inventoryText" + "text");
        inventoryIcon.GetComponentInParent<InventorySlot>().icon.sprite = ES3.Load<Sprite>("inventoryIcon");
        inventoryIcon.GetComponentInParent<InventorySlot>().icon.enabled = true;
        inventoryIcon.GetComponentInParent<InventorySlot>().removeButton.interactable = true;
        //Inventory Loading | SlotUI


        MP.Resume();





    }

  

    }

Image

https://imgur.com/a/B4CVZs2

As you can see my inventory icon and inventory slot GameObject variables are referenced as public GameObjects, and on my GameManager i have attached one of my inventory slots and it's child object icon in the inspecter. and by using that I am only able to inject the item Text/Sprite into one of my inventory slots, when my inventory actually contains 20 inventory slots in grand total. Any chance you'd know how i'd go about saving/loading the text/sprites of all of my inventory slots at once? I was thinking about using a list but not sure how to implement it :)?
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: How would I go about saving my Inventory Status?

Post by Joel »

Hi there,

With your code, you would need to add a for loop to iterate through each of the slots. And then each key that you save will need to append the slot number to the key, otherwise you will be constantly be overwriting the same keys.

Alternatively you could save Lists of each thing which you populate yourself (for example, a List<float> for hungerValue, a List<float> for restValue, etc). You would then iterate through these and assign them back to the appropriate slot.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
saambell1996
Posts: 5
Joined: Sat Jun 29, 2019 2:11 pm

Re: How would I go about saving my Inventory Status?

Post by saambell1996 »

think you'd be able to explain the loop/key iteration in a little bit more detail mate with perhaps an example? Just to make sure i'm getting this right :)?

Right now I have

Code: Select all

public void Save()
    {
       
        StatScripts statsRef = player.GetComponent<StatScripts>();
        MenuPause MP = canvas.GetComponent<MenuPause>();
        

        Debug.Log("Saved Game");
        ES3.Save<float>("hungerValue", statsRef.currentHunger);
        ES3.Save<float>("restValue", statsRef.currentRest);
        ES3.Save<float>("socialValue", statsRef.currentSocial);
        ES3.Save<float>("hygineValue", statsRef.currentHygine);
        ES3.Save<float>("bathroomValue", statsRef.currentBathroom);
        ES3.Save<float>("comfortValue", statsRef.currentComfort);

        foreach (GameObject i in inventorySlotArray )
        {
            ES3.Save<string>("inventoryText" + "text", i.GetComponent<InventorySlot>().itemText.text);
            ES3.Save<Sprite>("inventoryIcon", i.GetComponent<InventorySlot>().icon.sprite);
           
        }

      //  ES3.Save<InventorySlot>("inventoryKey", inventorySlot.GetComponent<InventorySlot>());
      //  ES3.Save<List<Item>>("inventoryItem", GetComponent<Inventory>().items);


        ES3.Save<string>("inventoryText" + "text", inventorySlot.GetComponent<InventorySlot>().itemText.text);
        ES3.Save<Sprite>("inventoryIcon", inventoryIcon.GetComponentInParent<InventorySlot>().icon.sprite);

        if(inventorySlot.GetComponent<InventorySlot>().enabled == false)
        {
            Debug.Log("Inventory is disabled but still saved with UpdateUI");
        }

        MP.Resume();




    }

    public void Load()
    {
        InventoryUI IUI = inventoryUI.GetComponent<InventoryUI>();
        MenuPause MP = canvas.GetComponent<MenuPause>();

        //Stats Loading

        StatScripts statsRef = player.GetComponent<StatScripts>();
        Debug.Log("Loaded Game");
        statsRef.currentHunger = ES3.Load<float>("hungerValue");
        statsRef.currentRest = ES3.Load<float>("restValue");
        statsRef.currentSocial = ES3.Load<float>("socialValue");
        statsRef.currentHygine = ES3.Load<float>("hygineValue");
        statsRef.currentBathroom = ES3.Load<float>("bathroomValue");
        statsRef.currentComfort = ES3.Load<float>("comfortValue");


        //Inventory Loading
        if (!ES3.KeyExists("inventoryKey"))
            return;

        ES3.LoadInto<InventorySlot>("inventoryKey", inventorySlot.GetComponent<InventorySlot>());

        GetComponent<Inventory>().items = ES3.Load<List<Item>>("inventoryItem");
        // IUI.UpdateUI();
        foreach (GameObject i in inventorySlotArray)
        {
          i.GetComponent<InventorySlot>().itemText.text = ES3.Load<string>("inventoryText" + "text");
          i.GetComponent<InventorySlot>().icon.sprite = ES3.Load<Sprite>("inventoryIcon");

        }
       // inventorySlot.GetComponent<InventorySlot>().itemText.text = ES3.Load<string>("inventoryText" + "text");
      //  inventoryIcon.GetComponentInParent<InventorySlot>().icon.sprite = ES3.Load<Sprite>("inventoryIcon");
     
        
        //  inventoryIcon.GetComponentInParent<InventorySlot>().icon.enabled = true;
      //  inventoryIcon.GetComponentInParent<InventorySlot>().removeButton.interactable = true;
        //Inventory Loading | SlotUI


        MP.Resume();





    }
Last quick question, if I wanted all my stats/inventory ect on another scene, would i copy my whole Hierachy from the first scene (character and EasySave manager included) and then paste it into the new scene and call the Load() function on awake of the new scene? :) Not sure if that would cause any duplicate issues?
If there's a better way to go about this i'd love to know
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: How would I go about saving my Inventory Status?

Post by Joel »

Hi Sam,

With your loops, you should also append something unique to the end of the key so we know that data belongs to that specific save slot (i.e. the number of the save slot). I.e.
foreach (GameObject i in inventorySlotArray )
{
    // Append the value of 'i' to the end of the key so we know what save slot this data belongs to.
    ES3.Save<string>("inventoryText" + "text" + i, i.GetComponent<InventorySlot>().itemText.text);
    ES3.Save<Sprite>("inventoryIcon" + i, i.GetComponent<InventorySlot>().icon.sprite);      
}
You would also need to do this for your load loop.

With regard to copying the scene, this should work, assuming that Unity can resolve the references correctly. I find that duplicating the scene itself rather than copy/pasting the contents tends to be more reliable.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
saambell1996
Posts: 5
Joined: Sat Jun 29, 2019 2:11 pm

Re: How would I go about saving my Inventory Status?

Post by saambell1996 »

Hey, seem to be getting this error when using my Load() function on awake of a scene so that I can load my stats/inventory

NullReferenceException: Object reference not set to an instance of an object
InventoryUI.UpdateUI () (at Assets/Scripts/Items/Inventory Scripts/InventoryUI.cs:38)
SimpleSave.Load () (at Assets/Scripts/ManagerScript/SimpleSave.cs:90)
LoadDataOnAwake.Awake () (at Assets/LoadDataOnAwake.cs:12)

The Inventory still seems to be saving/loading as it's meant to! But this error is making me feel like I'm doing it slightly wrong?
This is more current code to load my Inventory/stats, any idea what I could be doing wrong here :)?

Code: Select all


    public void UpdateUI()
    {
        InventorySlot[] slots = itemsParent.GetComponentsInChildren<InventorySlot>();
        for (int i = 0; i < slots.Length; i++)
        {
            if (i < inventory.items.Count)
            {

                slots[i].AddItem(inventory.items[i]);
               
               
               
            }
            else
            {
                slots[i].ClearSlot();
                
            }
        }
    }


Code: Select all

 public void Save()
    {
       
        StatScripts statsRef = player.GetComponent<StatScripts>();
        MenuPause MP = canvas.GetComponent<MenuPause>();
        

        Debug.Log("Saved Game");
        ES3.Save<float>("hungerValue", statsRef.currentHunger);
        ES3.Save<float>("restValue", statsRef.currentRest);
        ES3.Save<float>("socialValue", statsRef.currentSocial);
        ES3.Save<float>("hygineValue", statsRef.currentHygine);
        ES3.Save<float>("bathroomValue", statsRef.currentBathroom);
        ES3.Save<float>("comfortValue", statsRef.currentComfort);

        foreach (GameObject i in inventorySlotArray )
        {
            ES3.Save<string>("inventoryText" + "text" + i, i.GetComponent<InventorySlot>().itemText.text);
            ES3.Save<Sprite>("inventoryIcon" + i, i.GetComponent<InventorySlot>().icon.sprite);
           
        }

       ES3.Save<InventorySlot>("inventoryKey", inventorySlot.GetComponent<InventorySlot>());
       ES3.Save<List<Item>>("inventoryItem", GetComponent<Inventory>().items);


        if(inventorySlot.GetComponent<InventorySlot>().enabled == false)
        {
            Debug.Log("Inventory is disabled but still saved with UpdateUI");
        }

        MP.Resume();




    }

    public void Load()
    {
        InventoryUI IUI = inventoryUI.GetComponent<InventoryUI>();
        MenuPause MP = canvas.GetComponent<MenuPause>();

        //Stats Loading

        StatScripts statsRef = player.GetComponent<StatScripts>();
        Debug.Log("Loaded Game");
        statsRef.currentHunger = ES3.Load<float>("hungerValue");
        statsRef.currentRest = ES3.Load<float>("restValue");
        statsRef.currentSocial = ES3.Load<float>("socialValue");
        statsRef.currentHygine = ES3.Load<float>("hygineValue");
        statsRef.currentBathroom = ES3.Load<float>("bathroomValue");
        statsRef.currentComfort = ES3.Load<float>("comfortValue");


        //Inventory Loading
        if (!ES3.KeyExists("inventoryKey"))
            return;

        ES3.LoadInto("inventoryKey", inventorySlot.GetComponent<InventorySlot>());

        GetComponent<Inventory>().items = ES3.Load<List<Item>>("inventoryItem");
        IUI.UpdateUI();

        foreach (GameObject i in inventorySlotArray)
        {
          i.GetComponent<InventorySlot>().itemText.text = ES3.Load<string>("inventoryText" + "text");
          i.GetComponent<InventorySlot>().icon.sprite = ES3.Load<Sprite>("inventoryIcon");


         

        }
         
        // inventorySlot.GetComponent<InventorySlot>().itemText.text = ES3.Load<string>("inventoryText" + "text");
        //  inventoryIcon.GetComponentInParent<InventorySlot>().icon.sprite = ES3.Load<Sprite>("inventoryIcon");


        //Inventory Loading | SlotUI


        MP.Resume();





    }
User avatar
Joel
Moodkie Staff
Posts: 4846
Joined: Wed Nov 07, 2012 10:32 pm

Re: How would I go about saving my Inventory Status?

Post by Joel »

Hi there,

As the error isn't coming from Easy Save, it's not possible for me to provide assistance. However, you might want to check what variable is null and try to work back from that.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Post Reply