根据Kryzarel在YouTube上的教程,我一直在为库存系统统一实施保存系统。 这是访问序列化和反序列化以及与保存系统相关的其他脚本的代码。
FileReadWrite.cs
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public static class FileReadWrite
{
public static void WriteToBinaryFile<T>(string filePath , T objectToWrite)
{
using (Stream stream = File.Open(filePath, FileMode.Open))
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(stream, objectToWrite);
}
}
public static T ReadFromBinaryFile<T>(string filePath)
{
using (Stream stream = File.Open(filePath, FileMode.Open))
{
var binaryFormatter = new BinaryFormatter();
return (T)binaryFormatter.Deserialize(stream);
}
}
}
ItemSaveIO.cs
using UnityEngine;
public static class ItemSaveIO
{
private static readonly string baseSavePath;
static ItemSaveIO()
{
baseSavePath = Application.persistentDataPath;
}
public static void SaveItems(ItemContainerSaveData items, string fileName)
{
FileReadWrite.WriteToBinaryFile(baseSavePath + "/" + fileName + ".dat", items);
}
public static ItemContainerSaveData LoadItems(string fileName)
{
string filePath = baseSavePath + "/" + fileName + ".dat";
if(System.IO.File.Exists(filePath))
{
return FileReadWrite.ReadFromBinaryFile<ItemContainerSaveData>(filePath);
}
else
{
Debug.LogError("Save file not found in" + filePath);
return null;
}
}
}
ItemSaveData.cs
using System;
[Serializable]
public class ItemSlotSaveData
{
public string ItemID;
public ItemSlotSaveData(string itemID)
{
ItemID = itemID;
}
}
[Serializable]
public class ItemContainerSaveData
{
public ItemSlotSaveData[] SavedSlots;
public ItemContainerSaveData(int numItems)
{
SavedSlots = new ItemSlotSaveData[numItems];
}
}
ItemSaveManager.cs
using System.Collections;
using System;
using System.Collections.Generic;
using UnityEngine;
public class ItemSaveManager : MonoBehaviour
{
[SerializeField] ItemDatabase itemDatabase;
private const string InventoryFileName = "Inventory";
private const string EquipmentFileName = "Equipment";
public void LoadInventory(Character character)
{
ItemContainerSaveData savedSlots = ItemSaveIO.LoadItems(InventoryFileName);
if (savedSlots == null) return;
character.Inventory.Clear();
for(int i = 0; i < savedSlots.SavedSlots.Length; i++)
{
ItemSlot itemSlot = character.Inventory.ItemSlots[i];
ItemSlotSaveData savedSlot = savedSlots.SavedSlots[i];
if(savedSlot == null)
{
itemSlot.Item = null;
}
else
{
itemSlot.Item = itemDatabase.GetItemCopy(savedSlot.ItemID);
}
}
}
public void LoadEquipment(Character character)
{
ItemContainerSaveData savedSlots = ItemSaveIO.LoadItems(EquipmentFileName);
if (savedSlots == null) return;
foreach (ItemSlotSaveData savedSlot in savedSlots.SavedSlots)
{
if(savedSlots == null)
{
continue;
}
Item item = itemDatabase.GetItemCopy(savedSlot.ItemID);
character.Inventory.AddItem(item);
character.Equip((EquippableItem)item);
}
}
public void SaveInventory(Character character)
{
SaveItems(character.Inventory.ItemSlots, InventoryFileName);
}
public void SaveEquipment(Character character)
{
SaveItems(character.EquipmentPanel.EquipmentSlots, EquipmentFileName);
}
private void SaveItems(IList<ItemSlot> itemSlots, string fileName)
{
var saveData = new ItemContainerSaveData(itemSlots.Count);
for(int i = 0; i < saveData.SavedSlots.Length; i++)
{
ItemSlot itemSlot = itemSlots[i];
if(itemSlot.Item == null)
{
saveData.SavedSlots[i] = null;
}
else
{
saveData.SavedSlots[i] = new ItemSlotSaveData(itemSlot.Item.ID);
}
}
ItemSaveIO.SaveItems(saveData, fileName);
}
}
ItemDatabase.cs
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[CreateAssetMenu]
public class ItemDatabase : ScriptableObject
{
[SerializeField] Item[] items;
public Item GetItemReference(string itemID)
{
foreach(Item item in items)
{
if(item.ID == itemID)
{
return item;
}
}
return null;
}
public Item GetItemCopy(string itemID)
{
Item item = GetItemReference(itemID);
if (item == null) return null;
return item.GetCopy();
}
#if UNITY_EDITOR
private void OnValidate()
{
LoadItems();
}
private void OnEnable()
{
EditorApplication.projectWindowChanged -= LoadItems;
}
private void OnDisable()
{
EditorApplication.projectWindowChanged -= LoadItems;
}
private void LoadItems()
{
items = FindAssetsByType<Item>("Assets");
}
public static T[] FindAssetsByType<T>(params string[] folders) where T : Object
{
string type = typeof(T).ToString().Replace("UnityEngine.", "");
string[] guids;
if (folders == null || folders.Length == 0)
{
guids = AssetDatabase.FindAssets("t:" + type);
}
else
{
guids = AssetDatabase.FindAssets("t:" + type, folders);
}
T[] assets = new T[guids.Length];
for (int i = 0; i < guids.Length; i++)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
assets[i] = AssetDatabase.LoadAssetAtPath<T>(assetPath);
}
return assets;
}
#endif
}
在Character脚本Awake()函数中最后2条语句,
itemSaveManager.LoadEquipment(this);
itemSaveManager.LoadInventory(this);
和OnDestroy()函数中的2条语句
itemSaveManager.SaveEquipment(this);
itemSaveManager.SaveInventory(this);
这里可能出了什么问题? 另外,这是一个例外:
SerializationException: Attempting to deserialize an empty stream.
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck, System.Boolean isCrossAppDomain, System.Runtime.Remoting.Messaging.IMethodCallMessage methodCallMessage) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck, System.Runtime.Remoting.Messaging.IMethodCallMessage methodCallMessage) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
FileReadWrite.ReadFromBinaryFile[T] (System.String filePath) (at Assets/Scripts/SaveFile/FileReadWrite.cs:20)
ItemSaveIO.LoadItems (System.String fileName) (at Assets/Scripts/SaveFile/ItemSaveIO.cs:22)
ItemSaveManager.LoadEquipment (Character character) (at Assets/Scripts/SaveFile/ItemSaveManager.cs:40)
Character.Awake () (at Assets/Scripts/Character.cs:62)
答案 0 :(得分:0)
将FileMode
方法中的流的ReadFromBinaryFile
从FileMode.Create
更改为FileMode.Open
。
我猜想,当您使用ReadFromBinaryFile
调用FileMode.Create
时,会清除使用WriteToBinaryFile
创建的文件,这就是为什么您得到空流的原因。
这是它的外观:
public static class FileReadWrite
{
public static void WriteToBinaryFile<T>(string filePath , T objectToWrite)
{
using (Stream stream = File.Open(filePath, FileMode.Create))
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(stream, objectToWrite);
}
}
public static T ReadFromBinaryFile<T>(string filePath)
{
using (Stream stream = File.Open(filePath, FileMode.Open))
{
var binaryFormatter = new BinaryFormatter();
return (T)binaryFormatter.Deserialize(stream);
}
}
}