【问题标题】:Save List<GameObject> and load in unity保存 List<GameObject> 并统一加载
【发布时间】:2021-10-10 11:24:33
【问题描述】:

所以我有这个 List&lt;GameObject&gt; character; 那有我想在我的游戏中随机生成的所有角色的数据。在初始游戏中,我们只有一个数据,即此列表中的一个 GameObject。当我从商店购买角色时,他们会被添加到名为“角色”的列表中。现在所有这些都运行良好。问题是当我在购买角色后退出游戏并再次播放时,这些购买并没有被保存。我想知道一种保存和加载此list&lt;GameObject&gt; 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");
        }
    }

在这里保存角色模型并将其加载回使用 playerprefs 可以正常工作。我让它工作了,但问题是一旦我买了东西,它就会改变购买。除非退出游戏并再次回来,否则冷静下来。它变成了从购买到再次购买。尝试保存字符串,或者我什至不知道它是否正确。请帮助@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;
    }
}

【问题讨论】:

  • 这对您有帮助吗? stackoverflow.com/questions/10337410/…
  • 关于上面的链接,最相关的在this answer下,你应该考虑JSON序列化。
  • 你不能直接保存游戏对象,因为它们包含的状态必须绑定到当前会话(例如渲染器或当前位置)。相反,每个角色都应该有一个 ID。当玩家解锁新角色时,您应该保存当前角色的标识符 - 您可以使用逗号分隔的字符串,类似于 "character1_id,character2_id"。当会话开始时,读回这个值并正确地实例化字符。
  • @CoderCharmander 可以简要介绍一下如何实现这一目标
  • @DekuDesu 但我如何将列表数组存储到 json 中。我们通常不能在 json 上存储列表,对吧?

标签: c# list unity3d save


【解决方案1】:

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

所以我们需要先创建一个可以序列化的类来存储一些相关数据,并用“Serializable”属性对其进行标记。

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

然后我们将创建另一个可序列化的类来保存我们所有的各种保存数据,非常有创意地命名... 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;
        }
    }

}

【讨论】:

  • 感谢您的回答!!!我现在知道该怎么做.....我只是想知道Serializable 是如何将它放在一个班级之上的,它对那个班级有什么影响?我查看了统一文件,但无法给出明确的答案
  • 类的可序列化属性告诉 json 实用程序该类实际上是“可序列化的”,并在序列化为 json 时包含它。这是相关的统一文档docs.unity3d.com/Manual/JSONSerialization.html。您可以将其他类作为 SaveData 上没有该属性的字段包含在内,而 json 实用程序将忽略它们。
  • 很酷,现在解决了我所有的疑问。非常感谢@dgates82 和所有帮助过我的人。
  • 试过你的解决方案无法弄清楚如何用我的商店脚本来实现这一点。我已经用商店脚本更新了我的问题
  • @IcyThug 我没有看到您使用任何代码更新了问题?
【解决方案2】:

您可以使用Json.net 来序列化您的对象,或者使用unity 中的JsonUtility 来提高性能

如果您想将数组与JsonUtility 一起使用,您可以编写一个类来定义您存储类的方式

【讨论】:

  • 这更像是一个评论而不是一个答案。答案必须真正回答问题,而不是仅仅将提问者指向可能帮助他们解决问题的资源/关键字。 How to Answer
猜你喜欢
  • 2021-10-15
  • 2021-05-22
  • 1970-01-01
  • 1970-01-01
  • 2015-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-28
相关资源
最近更新 更多