保存和加载库存

时间:2019-06-26 17:46:45

标签: c# unity3d save load inventory

我已经创建了清单,并且一切正常。现在,我应该实现库存的保存和加载(在文件中)。但是,我发现自己陷入了困境。我当时正在考虑创建一个库存数据脚本来获取可序列化的数据,然后保存它。我没有使用可编写脚本的对象。您对我有什么建议吗?下面是库存代码。

 public class Inventory2 : MonoBehaviour
 {
     public bool inventoryEnabled;
     public GameObject inventory, slotHolder;
     private Transform[] slot;
     public int level;

     void Start()
     {
         level = SceneManager.GetActiveScene().buildIndex;
         GetAllSlots();        
     }   

     void Update()
     {
         if (Input.GetAxis("Inventory") != 0)
         {
             inventoryEnabled = !inventoryEnabled;
         }

         if(inventoryEnabled)
         {
             inventory.SetActive(true);
         }
         else
         {
             inventory.SetActive(false);
         }
     }

     public void OnTriggerEnter(Collider other)
     {
         if (other.tag == "Clues")
         {            
             AddClue(other.GetComponent<Clue2>());            
         }
     }
     public void AddClue(Clue2 clue)
     {
         Text description = clue.GetComponent<Text>();

         for (int i = 0; i < 2; i++)
         {
             if(slot[i].GetComponent<Slot2>().empty == true && clue.pickedUp == false)
             {
                 slot[i].GetComponent<Slot2>().clue = clue;
                 slot[i].GetComponent<Slot2>().descriptionFinal = description;
                 slot[i].GetComponent<Slot2>().empty = false;
                 clue.GetComponent<Clue2>().pickedUp = true;
             }
         }
     }

     public void GetAllSlots()
     {
         slot = new Transform[Clue2.objects];

         for(int i = 0; i < Clue2.objects; i++)
         {
             slot[i] = slotHolder.transform.GetChild(i);
             slot[i].GetComponent<Slot2>().empty = true;
         }
     }
 }
 public class Slot2 : MonoBehaviour
 {
     public Clue2 clue;
     public bool empty;
     public Text descriptionFirst, descriptionFinal;

     void Awake()
     {

     }

     void Update()
     {
         if (clue)
         {
             this.GetComponentInChildren<Text>().text = descriptionFinal.text;
         }
         else
         {
             this.GetComponentInChildren<Text>().text = descriptionFirst.text;
             empty = true;
         }
     }
 }
 public class Clue2 : MonoBehaviour
 {
     public static int objects = 0;
     public static int objectsCollected = 0;
     public Text description;
     public bool pickedUp;

     public GameObject cluePopUpPanel, canvasCluesPanel;
     public Canvas canvasHUD;
     public static bool activeClue = false;

     void Awake()
     {
         objects++;
     }

     void Update()
     {
         if (canvasCluesPanel.gameObject.activeSelf == true && Input.GetAxis("PickUp") != 0)
         {
             activeClue = true;
             cluePopUpPanel.gameObject.GetComponent<UnityEngine.UI.Text>().text = description.text;
         }
     }

     private void OnTriggerEnter(Collider other)
     {
         if (other.tag == "Player")
         {
             if (canvasCluesPanel.gameObject.activeSelf == false)
             {
                 canvasCluesPanel.gameObject.SetActive(true);                
             }
         }
     }

     private void OnTriggerStay(Collider other)
     {
         if (other.tag == "Player" && activeClue == true)
         {
             cluePopUpPanel.gameObject.SetActive(true);
             cluePopUpPanel.GetComponentInChildren<Text>().text = this.GetComponent<Text>().text;
             canvasCluesPanel.gameObject.SetActive(false);
         }

         if (other.tag == "Player" && activeClue == false)
         {
             cluePopUpPanel.gameObject.SetActive(false);
             canvasCluesPanel.gameObject.SetActive(true);
         }        
     }

     private void OnTriggerExit(Collider other)
     {
         if (other.tag == "Player" && activeClue == true)
         {
             cluePopUpPanel.gameObject.SetActive(false);
             canvasCluesPanel.gameObject.SetActive(false);
             activeClue = false;
             if(objectsCollected < objects)
             {
                 objectsCollected++;
             }
         }
         else
         {
             cluePopUpPanel.gameObject.SetActive(false);
             canvasCluesPanel.gameObject.SetActive(false);
             activeClue = false;
         }

         canvasHUD.gameObject.SetActive(true);
     }
 }

2 个答案:

答案 0 :(得分:0)

有多种保存/加载数据的方法。 通常,它涉及将数据序列化到某处的某个存储(内存,硬盘驱动器,网络中的服务器等),然后读取它。

我发现最简单的解决方案是使用JSON序列化(JSON.NET for Unity是一个很好的解决方案)。

假设我的库存存储为List<Item> Inventory

您用Item标记JsonObjectAttribute(MemberSerialization.OptIn)类(以避免序列化从MonoBehaviour继承来的内容),并且要存储的每个属性都用JsonPropertyAttribute装饰

然后,您可以将数据序列化为PlayerPrefs(基本上是将数据存储在本地设备上的抽象方法),然后从那里加载回去。

伪代码:

private const string PlayerStatePlayerPrefKey = "PlayerSaveData";

void SavePlayerState(PlayerState state)
{
    var serializedState = JsonConvert.Serialize(state);
    PlayerPrefs.SetString(PlayerStatePlayerPrefKey, serializedState);
    PlayerPrefs.Save();
}

PlayerState LoadPlayerState()
{
    var serializedState = PlayerPrefs.GetString(PlayerStatePlayerPrefKey, null);
    if (serializedState == null)
        return new PlayerState();
    return JsonConvert.DeserializeObject<PlayerState>(serializedState);
}

您应该确保将整个过程包装在Try... Catch中,并处理可能发生的序列化问题-最常见的一个问题是对保存数据的数据结构进行更改会阻止读取旧的保存。

我还建议将库存数据存储在POCO(普通的C#对象)中,并将数据和逻辑与游戏的视觉效果分开。


不相关的是,我假设您是编程的新手,我建议您阅读SOLID principle,并读一本好书Clean Code

我发现一些代码标准可以清理代码:

使用if (boolean == true)代替if(boolean)

return ifs求逆并合并以删除嵌套:

private void OnTriggerEnter(Collider other)
{
    if (other.tag == "Player")
    {
        if (canvasCluesPanel.gameObject.activeSelf == false)
        {
            canvasCluesPanel.gameObject.SetActive(true);                
        }
    }
}

转到

private void OnTriggerEnter(Collider other)
{
    if (!other.tag == "Player" || !canvasCluesPanel.gameObject.activeSelf)
        return;
    canvasCluesPanel.gameObject.SetActive(true);    
}

恕我直言,制作游戏非常复杂,拥有更简洁,更可维护的代码将大大有助于您的开发。

祝你好运

答案 1 :(得分:0)

here可以找到一个很好的教程。

1-创建数据类型

作为一个好习惯,将要保存的数据存储在一个类中。像这样创建SlotData。因此,尝试像这样转换它们。此类必须具有属性System.Serializable

一个例子就是这个

[System.Serializable]
public class SlotData
{
    public bool containsItem = false;
    public string Description;
    //other possible elements
    public int amount; 
}

InventoryData只是SlotData的数组。

[System.Serializable]
public class InventoryData
{
    public SlotData[] inventorySlots;
}

2-在保存之前设置数据

您可以在代码中的某个地方更新清单。您必须设置此数据,可能来自单行为。基本上,您可以在班级中添加所有想要的信息。

public void PrepareToSave()
{
    //setup the inventory
    var yourInventory = new InventoryData();
    //fill all the slots, you must also calculate their amount
    yourInventory.inventorySlots = new SlotData[CalculateYourAmount];
    //fill all the slots here
    for (int   i= 0; i  < CalculateYourAmount;  i++)
    {   
        //fill all the slots
        yourInventory.inventorySlots[i] = CreateTheSlot();
    }

    //this go to the next step
    SaveYourInventory(yourInventory);
}

3-保存库存

根据您的要求,您可以使用BinaryFormatterFileStream

private void SaveYourInventory(InventoryData yourInventory)
{
    var             savePath = Application.persistentDataPath + "/inventory.dat";
    BinaryFormatter bf       = new BinaryFormatter();
    FileStream      file     = File.Create(savePath);
    bf.Serialize(file, yourInventory);
    file.Close();
}

4-加载库存

然后,您可以像这样加载广告资源。

public void LoadYourInventory()
{
    var savePath = Application.persistentDataPath + "/inventory.dat";
    if (File.Exists(savePath))
    {
        BinaryFormatter bf   = new BinaryFormatter();
        FileStream      file = File.Open(savePath, FileMode.Open);
        InventoryData yourInventory = (InventoryData) bf.Deserialize(file);
        file.Close();

        //do what you want with the inventory
        ...
    }
}

您还可以找到Save Manager系统here

如果您想更高级,则可以使用此optimized formatter(或者,如果您返回到保存/加载业务,则可以将其保存以备将来使用。)