(Unity3D) 保存/加载玩家库存

Posted

技术标签:

【中文标题】(Unity3D) 保存/加载玩家库存【英文标题】:(Unity3D) Saving/Loading Player Inventory 【发布时间】:2018-01-07 00:52:20 【问题描述】:

每当通过播放器脚本保存或加载时,库存总是两边都是空白的,我不知道问题出在哪里。库存使用 Unity 中级库存教程中使用的相同系统,该系统运行良好,但加载时库存始终为空白。

播放器

public void Save()
    Debug.Log ("Saving data (player / inventory)");

    SaveLoadManager.SavePlayer (this); //works fine
    SaveLoadInventory.SaveInventory (inv); //where the issue is

public void Load()
    Debug.Log ("Loading data (player / inventory)");

    float[] loadedStats = SaveLoadManager.LoadPlayer (); //works fine
    Item[] loadedItems = SaveLoadInventory.LoadInventory (); //comes up empty

    inv.items = loadedItems; //try to set the referenced Inventory script array of items equal to the loaded items

    //loadedStats[...] are set to player values and all works good

库存

public class Inventory : MonoBehaviour 

public Image[] itemImages = new Image[numItemSlots];
public Text[] itemAmountsText = new Text[numItemSlots];
public int[] itemAmounts = new int[numItemSlots];
public Item[] items = new Item[numItemSlots];

public const int numItemSlots = 40;

public void AddItem(Item itemToAdd)
    for (int i = 0; i < items.Length; i++) 
        if(items [i] == itemToAdd)
            if (itemAmounts [i] <= 99) 
                itemAmounts [i]++;
             else 
                items [i] = itemToAdd;
                itemImages [i].sprite = itemToAdd.itemImage;
                itemImages [i].enabled = true;
            
            itemAmountsText [i].text = itemAmounts [i] + "";
            return;
         else if (items [i] == null) 
            itemAmounts [i]++;
            itemAmountsText [i].text = itemAmounts [i] + "";
            items [i] = itemToAdd;
            itemImages [i].sprite = itemToAdd.itemImage;
            itemImages [i].enabled = true;
            return;
        
    


public void RemoveItem(Item itemToRemove)
    for (int i = 0; i < items.Length; i++) 
        if (items [i] == itemToRemove) 
            items [i] = null;
            itemImages [i].sprite = null;
            itemImages [i].enabled = false;
            return;
        
    


public void ClearAllItems()
    for (int i = 0; i < items.Length; i++) 
        items [i] = null;
        itemImages [i].sprite = null;
        itemImages [i].enabled = false;
        return;
    


[Serializable]
public class InventoryData 
    public Image[] itemImages = new Image[numItemSlots];
    public Text[] itemAmountsText = new Text[numItemSlots];
    public int[] itemAmounts = new int[numItemSlots];
    public Item[] items;

    public InventoryData(Inventory inv)
        items = new Item[numItemSlots];

        for (int i = 0; i < items.Length; i++) 
            inv.items[i] = items[i];
            inv.itemAmounts[i] = itemAmounts[i];
            inv.itemAmountsText[i] = itemAmountsText[i];
            inv.itemImages[i] = itemImages[i];
            
        
    

SaveLoadInventory

public static class SaveLoadInventory 

    public static void SaveInventory(Inventory inv)

    BinaryFormatter bf = new BinaryFormatter();
    var createdir = Directory.CreateDirectory(Application.persistentDataPath + "/saves");
    String dir = Application.persistentDataPath + "/saves";
    Inventory.InventoryData data = new Inventory.InventoryData (inv);

    DirectoryInfo dir_i = new DirectoryInfo(dir);
    FileInfo[] info = dir_i.GetFiles("*.inv");
    foreach (FileInfo f in info) 
        Debug.Log (f);
    

    FileStream stream = new FileStream (dir + "/player" + (info.Length + 1) + ".inv", FileMode.Create);

    bf.Serialize (stream, data);
    stream.Close ();



    public static Item[] LoadInventory()
    String dir = Application.persistentDataPath + "/saves";

    DirectoryInfo dir_i = new DirectoryInfo(dir);
    FileInfo[] info = dir_i.GetFiles("*.inv");
    foreach (FileInfo f in info) 
        Debug.Log (f);
    

    if (info.Length >= 1) 

        BinaryFormatter bf = new BinaryFormatter ();
        var createdir = Directory.CreateDirectory(Application.persistentDataPath + "/saves");
        FileStream stream = new FileStream (dir + "/player" + (info.Length) + ".inv", FileMode.Open);
        Inventory.InventoryData data = bf.Deserialize (stream) as Inventory.InventoryData;

        stream.Close ();
        return data.items;
     else 
        return null;
    

老实说,我会为此尝试一些调试组合并自己弄清楚,但这一切似乎都太复杂了,无法将我的头包裹起来,并且一直担心它永远不会起作用。如果您有任何帮助或建议,将不胜感激。

【问题讨论】:

【参考方案1】:

但加载时库存始终为空白。

第一个问题是您要在InventoryData 类中序列化的变量类型。您不能直接序列化 ImageText 组件,因为它们是 UnityEngine.Object 类型。对于Image 组件,保存Image 组件正在使用的Sprite 的路径。对于Text 组件,保存包含文本本身的.text 属性的值。

如果您想为ImageText 组件序列化多个属性,最好创建一个新的可序列化类/包装器,它将保存您要为Image 序列化的重要数据组件和另一个用于Text 组件。

例如,假设您要序列化有关Text 组件的信息,例如字体大小、实际文本和颜色,这里是一个简单的包装器示例:

[Serializable]
public class TextWrapper

    public int fontSize;
    public string text;
    public Color color;

    public TextWrapper(Text text)
    

        //Get Text Info
        fontSize = text.fontSize;
        this.text = text.text;
        color = text.color;
    

要序列化的Text 组件:

public Text myText;

您可以从Text 组件创建TextWrapper 的新实例以进行序列化:

TextWrapper textWrapper = new TextWrapper(myText);

现在,您可以在问题中使用BinaryFormatter 对其进行序列化。您可以将 TextWrapper 变量放入您的 InventoryData 类中,它也应该序列化。

注意TextWrapper 必须放在自己的.cs 文件中,否则BinaryFormatter 可能会对其进行序列化/反序列化。如果您在使用 .. 时仍有问题,请使用 Json。请参阅this 发布更多信息。

【讨论】:

以上是关于(Unity3D) 保存/加载玩家库存的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D 从 XML 文件保存和加载数据不起作用

unity3d (2d!) - 相机以玩家为中心,但永远不会超过“地图”边界

Unity3d:在屏幕边缘显示其他玩家/敌人/物体的方位

UNITY3D:如何将 NavMeshAgent 行走速度设置为 0,但让他向玩家旋转

Unity 3d 保存高分错误

Unity3d中制作Loading场景进度条所遇到的问题 LoadLevelAsync,AsyncOperation