IcyThug
IcyThug

Reputation: 48

Save List<GameObject> and load in unity

So I have this List<GameObject> character; That has data of all the characters I want to spawn in my game randomly. At the initial game, we only have one data i.e., one GameObject in this list. And as I buy characters from the shop they get added to this list named "characters". All of that is working fine now. The problem is when I quit the game after purchasing the characters and play-back again those purchases are not being saved. I wanna know a way of both saving and loading this list<GameObject> characters. I tried PlayerPrefs But it doesn't seem to perform saving of lists and that's the only way I know how to save in unity. Any help with the code. I have seen string delimiter (if I'm pronouncing it right) will do the job. But don't know what it will do and how to do can anyone help me. Thanks.

Updated Question Here is my shop script code:

[System.Serializable] public class ShopItem
    {
        public Sprite CharacterImage;
        public GameObject charactersModel;
        public int Price;
        public bool isPurchased = false;
    }

    public List<ShopItem> ShopItemsList;

    [Header("Panel Holders")]
    [SerializeField] GameObject MainMenu;
    [SerializeField] GameObject SettingsHolder;
    [SerializeField] GameObject ShopPanel;

    [Space]
    [Header("Item Template & Display")]
    GameObject ItemTemplate;
    GameObject g;
    [SerializeField] Transform ShopScrollView;
    Button buyBtn;

    //String Delimiter for Characters is "/"
    public char mCharacterDemiliter = '/';
    GameObject getGameManager;
    GameManager GameManagerRef;
    public string mPurchasedCharaters = string.Empty;
    public string[] mPurchasesCharacterList;

    private void Start()
    {
        getGameManager = GameObject.Find("GameManager");
        GameManagerRef = getGameManager.GetComponent<GameManager>();
        ItemTemplate = ShopScrollView.GetChild(0).gameObject;

        var length = ShopItemsList.Count;
        for (int i = 0; i < length; i++)
        {
            g = Instantiate(ItemTemplate, ShopScrollView);
            g.transform.GetChild(0).GetComponent<Image>().sprite = ShopItemsList[i].CharacterImage;
            g.transform.GetChild(1).GetComponentInChildren<TextMeshProUGUI>().text = ShopItemsList[i].Price.ToString();
            buyBtn = g.transform.GetChild(2).GetComponent<Button>();
            if (ShopItemsList[i].isPurchased)
            {
                DisableBuyButton();
            }
            buyBtn.AddEventListener(i, OnShopItemBtnClicked);
        }
        Destroy(ItemTemplate);
    }

    private void OnEnable()
    {
        //GameManagerRef.isCharacterPurchased = PlayerPrefs.GetString("SaveCharacterPurchaseData");
        GameManagerRef.mPurchasedCharaters = PlayerPrefs.GetString("SavedCharacters");
        mPurchasesCharacterList = mPurchasedCharaters.Split(mCharacterDemiliter);

        foreach (var sub in mPurchasesCharacterList)
        {
            ShopItemsList[].isPurchased = true;
            Debug.Log(int.Parse(sub));
            DisableBuyButton();
        }
    }

    public void DisableBuyButton()
    {
        buyBtn.interactable = false;
        buyBtn.transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = "PURCHASED";
        //Destroy(buyBtn);
    }

    public void OpenShop()
    {
        MainMenu.SetActive(false);
        SettingsHolder.SetActive(false);
        ShopPanel.SetActive(true);
    }

    public void ReturnButton()
    {
        MainMenu.SetActive(true);
        SettingsHolder.SetActive(true);
        ShopPanel.SetActive(false);
    }

    int boolToInt(bool val)
    {
        if (val)
            return 1;
        else
            return 0;
    }

    

    void OnShopItemBtnClicked(int itemIndex)
    {
        if (GameManagerRef.HasEnoughCoins(ShopItemsList[itemIndex].Price))
        {
            //purchase Item
            GameManagerRef.UseCoins(ShopItemsList[itemIndex].Price);
            ShopItemsList[itemIndex].isPurchased = true;
            buyBtn = ShopScrollView.GetChild(itemIndex).GetChild(2).GetComponent<Button>();
            GameManagerRef.character.Add(ShopItemsList[itemIndex].charactersModel);
            DisableBuyButton();

            #region "Save the purchased character"
            
            GameManagerRef.mPurchasedCharaters = GameManagerRef.mPurchasedCharaters + GameManagerRef.mCharacterDemiliter + ShopItemsList[itemIndex].charactersModel.name;
            PlayerPrefs.SetString("SavedCharacters", GameManagerRef.mPurchasedCharaters);

            GameManagerRef.isCharacterPurchased = GameManagerRef.isCharacterPurchased + GameManagerRef.mCharacterDemiliter + ShopItemsList[itemIndex].isPurchased;
            PlayerPrefs.SetString("SaveCharacterPurchaseData", GameManagerRef.isCharacterPurchased);
            //PlayerPrefs.SetInt("SaveCharacterPurchaseData", boolToInt(ShopItemsList[itemIndex].isPurchased));
            
            #endregion
        }
        else
        {
            Debug.Log("You dont have sufficient amount");
        }
    }

Here saving the character model and loading it back is working fine with use of playerprefs. I got it working but the problem is once I buy something its changes to purchased. cool until that unless when quitting the game and coming back again. Its become from purchased to Buy again. tried saving strings or i dont even know if its the right way. Help please @dgates82.

For reference my GameManager Script too coz i keep using them simultaneously

    public static GameManager Instance;
    //Stores Coin Value throughout the game
    /*[HideInInspector]*/ public int coin = 0;

    //Retreive / Load Coin Value thats saved
    int savedCoin;

    //Get the LevelManager of that specific scene
    [HideInInspector] public GameObject LevelManagerRef;
    /*[HideInInspector]*/ public GameObject shopRef;
    /*[HideInInspector]*/ public Shop storeShopScript;
    
    public List<GameObject> character = new List<GameObject>();
    
    [Space]
    [Header("Save and load store Character Data")]
    public string mPurchasedCharaters = string.Empty;
    public string isCharacterPurchased = string.Empty;

    public string[] mPurchasesCharacterList;
    public string[] characterPurchasedList;

    public char mCharacterDemiliter = '/';

    void Awake()
    {
        //Loads the coin save Data
        LoadCoinSaveData();

        #region "Singleton Code"
        //Checks to see if there is another gameManager instance and deletes that
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(this); //Singleton
            return;
        }
        Destroy(this.gameObject);
        #endregion

    }

    private void Start()
    {
        shopRef = FindInActiveObjectByName("ShopMenu");
        storeShopScript = shopRef.GetComponent<Shop>();
        PopulateCharacters();
        //makePurchased();
    }

    #region "Populate SavedCharacters"
    public void PopulateCharacters()
    {
        mPurchasedCharaters = PlayerPrefs.GetString("SavedCharacters");
        
        if(mPurchasedCharaters == string.Empty)
        {
            mPurchasedCharaters = "Man";
            PlayerPrefs.SetString("SavedCharacters", mPurchasedCharaters);
        }
        mPurchasesCharacterList = mPurchasedCharaters.Split(mCharacterDemiliter);

        foreach (var sub in mPurchasesCharacterList)
        {
            GameObject loadedCharacter =  Resources.Load(sub) as GameObject;
            character.Add(loadedCharacter);
        }
    }

    //public void makePurchased()
    //{
    //    isCharacterPurchased = PlayerPrefs.GetString("SaveCharacterPurchaseData");

    //    characterPurchasedList = isCharacterPurchased.Split(mCharacterDemiliter);

    //    foreach (string characterSaved in characterPurchasedList)
    //    {
    //        for (int i = 0; i <= storeShopScript.ShopItemsList.Count; i++)
    //        {
    //            if ()
    //            {
    //                storeShopScript.ShopItemsList[i].isPurchased = Convert.ToBoolean(bool.Parse(characterSaved));       
    //            }
    //        }
    //    }
    //}
    #endregion
   
    private void LoadCoinSaveData()
    {
        savedCoin = PlayerPrefs.GetInt("SavedCoinValue");
        coin = savedCoin;
    }

    //Reduce the coin value depending upon the amount
    public void UseCoins(int amount)
    {
        coin -= amount;
    }  
    
    //Use coins if enough coins are there?
    public bool HasEnoughCoins(int amount)
    {
        return (coin >= amount);
    }

    GameObject FindInActiveObjectByName(string name)
    {
        Transform[] objs = Resources.FindObjectsOfTypeAll<Transform>() as Transform[];
        for (int i = 0; i < objs.Length; i++)
        {
            if (objs[i].hideFlags == HideFlags.None)
            {
                if (objs[i].name == name)
                {
                    return objs[i].gameObject;
                }
            }
        }
        return null;
    }
}

Upvotes: 0

Views: 6868

Answers (1)

dgates82
dgates82

Reputation: 418

As mentioned in the comments, you cannot serialize GameObjects, or anything that inherits from MonoBehaviour for that matter.

So we need to start by creating a class that we can serialize to store some relevant data and mark it with a "Serializable" attribute.

[Serializable]
public class CharacterData // Do not inherit from MonoBehaviour here
{
    public int Id;
    public string Name;
    public int HitPoints;
    public int AttackDamage;
}

And your character game objects will use this data

public Character : MonoBehaviour
{
    public CharacterData Data;
    private int hitPoints;

    // Retrieve data from CharacterData for local use
    void Awake()
    {
        hitPoints = Data.HitPoints;
    }
}

Then we'll create another serializable class to hold all of our various save data, very creatively named... SaveData, which has a list of CharacterData as a field.

[Serializable]
public class SaveData // Do not inherit from MonoBehaviour here
{
    public List<CharacterData> Characters;
}

Now you can serialize and deserialize your SaveData and store it wherever you want

public class SaveManager : MonoBehavior
{
    public void Save(SaveData data)
    {
        // Serialize to json
        var jsonData = JsonUtility.ToJson(data);

        // Now save the json locally, to GPGS, etc. as you choose
    }

    public void Load()
    {
        // Retrieve json data from storage of your choice
        var jsonData = somehow get from storage
        
        // Then deserialize it back to an object
        var saveData = JsonUtility.FromJson<SaveData>(jsonData);

        // And then apply it to your characters
        // For example, maybe you instantiate your prefabs and then add the character data
        foreach (var characterData in saveData.Characters)
        {
            var spawnPoint = // need a spawn point
            var characterPrefab = // need a prefab to assign
            var character = Instantiate(characterPrefab, spawnPoint);
            // Assign the saved character data to the new game object
            character.CharacterData = characterData;
        }
    }

}

Upvotes: 2

Related Questions