保存列表<GameObject>并统一加载

Save List<GameObject> and load in unity

所以我有这个 List<GameObject> character; 里面有我想在游戏中随机生成的所有角色的数据。在初始游戏中,我们只有一个数据,即此列表中的一个 GameObject。当我从商店购买角色时,他们会被添加到名为“角色”的列表中。所有这些现在都工作正常。问题是当我在购买角色后退出游戏并再次播放时,这些购买没有被保存。我想知道一种保存和加载此 list<GameObject> characters 的方法。我试过 PlayerPrefs 但它似乎没有执行 lists 的保存,这是我知道如何统一保存的唯一方法。对代码的任何帮助。我已经看到字符串定界符(如果我发音正确的话)可以完成这项工作。但不知道它会做什么以及如何做任何人都可以帮助我。谢谢。

更新问题 这是我的商店脚本代码:

[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");
        }
    }

此处保存角色模型并将其加载回去可以正常使用播放器偏好设置。我让它工作了,但问题是一旦我买了东西,它就会改变购买。除非退出游戏并再次回来,否则很酷。它再次从购买变为购买。尝试保存字符串,否则我什至不知道它是否正确。请帮忙@dgates82.

参考我的GameManager脚本因为我一直同时使用它们

    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;
    }
}

如评论中所述,您不能序列化 GameObjects,或任何从 MonoBehaviour 继承的东西。

所以我们需要先创建一个class,我们可以序列化它来存储一些相关数据,并用“可序列化”属性标记它。

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

并且您的角色游戏对象将使用此数据

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

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

然后我们将创建另一个可序列化的 class 来保存我们所有的各种保存数据,非常有创意的命名... SaveData,它有一个 CharacterData 列表作为字段。

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

现在您可以序列化和反序列化您的 SaveData 并将其存储在任何您想要的地方

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;
        }
    }

}