Reputation: 48
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
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