简体   繁体   English

保存游戏状态的最佳方法是什么?

[英]What is the best way to save game state?

I find the best way to save game data in Unity3D Game engine.我找到了在 Unity3D 游戏引擎中保存游戏数据的最佳方法。
At first, I serialize objects using BinaryFormatter .首先,我使用BinaryFormatter序列化对象。

But I heard this way has some issues and is not suitable for save.但是听说这种方式有问题,不适合保存。
So, What is the best or recommended way for saving game state?那么,保存游戏状态的最佳或推荐方法是什么?

In my case, save format must be byte array.就我而言,保存格式必须是字节数组。

But I heard this way has some issues and not suitable for save.但是听说这种方式有问题,不适合保存。

That's right.这是正确的。 On some devices, there are issues with BinaryFormatter .在某些设备上, BinaryFormatter存在问题。 It gets worse when you update or change the class.当您更新或更改课程时,情况会变得更糟。 Your old settings might be lost since the classes non longer match.由于类不再匹配,您的旧设置可能会丢失。 Sometimes, you get an exception when reading the saved data due to this.有时,由于此原因,您在读取保存的数据时会出现异常。

Also, on iOS, you have to add Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");此外,在 iOS 上,您必须添加Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes"); or you will have problems with BinaryFormatter .否则您将遇到BinaryFormatter问题。

The best way to save is with PlayerPrefs and Json .最好的保存方法是使用PlayerPrefsJson You can learn how to do that here .你可以在这里学习如何做到这一点

In my case, save format must be byte array就我而言,保存格式必须是字节数组

In this case, you can convert it to json then convert the json string to byte array.在这种情况下,您可以将其转换为 json,然后将 json string转换为byte数组。 You can then use File.WriteAllBytes and File.ReadAllBytes to save and read the byte array.然后您可以使用File.WriteAllBytesFile.ReadAllBytes来保存和读取字节数组。

Here is a Generic class that can be used to save data.这是一个可用于保存数据的 Generic 类。 Almost the-same as this but it does not use PlayerPrefs .几乎-同,但它使用PlayerPrefs It uses file to save the json data.它使用文件来保存 json 数据。

DataSaver class: DataSaver类:

public class DataSaver
{
    //Save Data
    public static void saveData<T>(T dataToSave, string dataFileName)
    {
        string tempPath = Path.Combine(Application.persistentDataPath, "data");
        tempPath = Path.Combine(tempPath, dataFileName + ".txt");

        //Convert To Json then to bytes
        string jsonData = JsonUtility.ToJson(dataToSave, true);
        byte[] jsonByte = Encoding.ASCII.GetBytes(jsonData);

        //Create Directory if it does not exist
        if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
        }
        //Debug.Log(path);

        try
        {
            File.WriteAllBytes(tempPath, jsonByte);
            Debug.Log("Saved Data to: " + tempPath.Replace("/", "\\"));
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed To PlayerInfo Data to: " + tempPath.Replace("/", "\\"));
            Debug.LogWarning("Error: " + e.Message);
        }
    }

    //Load Data
    public static T loadData<T>(string dataFileName)
    {
        string tempPath = Path.Combine(Application.persistentDataPath, "data");
        tempPath = Path.Combine(tempPath, dataFileName + ".txt");

        //Exit if Directory or File does not exist
        if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
        {
            Debug.LogWarning("Directory does not exist");
            return default(T);
        }

        if (!File.Exists(tempPath))
        {
            Debug.Log("File does not exist");
            return default(T);
        }

        //Load saved Json
        byte[] jsonByte = null;
        try
        {
            jsonByte = File.ReadAllBytes(tempPath);
            Debug.Log("Loaded Data from: " + tempPath.Replace("/", "\\"));
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed To Load Data from: " + tempPath.Replace("/", "\\"));
            Debug.LogWarning("Error: " + e.Message);
        }

        //Convert to json string
        string jsonData = Encoding.ASCII.GetString(jsonByte);

        //Convert to Object
        object resultValue = JsonUtility.FromJson<T>(jsonData);
        return (T)Convert.ChangeType(resultValue, typeof(T));
    }

    public static bool deleteData(string dataFileName)
    {
        bool success = false;

        //Load Data
        string tempPath = Path.Combine(Application.persistentDataPath, "data");
        tempPath = Path.Combine(tempPath, dataFileName + ".txt");

        //Exit if Directory or File does not exist
        if (!Directory.Exists(Path.GetDirectoryName(tempPath)))
        {
            Debug.LogWarning("Directory does not exist");
            return false;
        }

        if (!File.Exists(tempPath))
        {
            Debug.Log("File does not exist");
            return false;
        }

        try
        {
            File.Delete(tempPath);
            Debug.Log("Data deleted from: " + tempPath.Replace("/", "\\"));
            success = true;
        }
        catch (Exception e)
        {
            Debug.LogWarning("Failed To Delete Data: " + e.Message);
        }

        return success;
    }
}

USAGE :用法

Example class to Save :要保存的示例类

[Serializable]
public class PlayerInfo
{
    public List<int> ID = new List<int>();
    public List<int> Amounts = new List<int>();
    public int life = 0;
    public float highScore = 0;
}

Save Data:保存数据:

PlayerInfo saveData = new PlayerInfo();
saveData.life = 99;
saveData.highScore = 40;

//Save data from PlayerInfo to a file named players
DataSaver.saveData(saveData, "players");

Load Data:加载数据:

PlayerInfo loadedData = DataSaver.loadData<PlayerInfo>("players");
if (loadedData == null)
{
    return;
}

//Display loaded Data
Debug.Log("Life: " + loadedData.life);
Debug.Log("High Score: " + loadedData.highScore);

for (int i = 0; i < loadedData.ID.Count; i++)
{
    Debug.Log("ID: " + loadedData.ID[i]);
}
for (int i = 0; i < loadedData.Amounts.Count; i++)
{
    Debug.Log("Amounts: " + loadedData.Amounts[i]);
}

Delete Data:删除数据:

DataSaver.deleteData("players");

I know this post is old, but in case other users also find it while searching for save strategies, remember:我知道这篇文章很旧,但如果其他用户在搜索保存策略时也发现了它,请记住:

PlayerPrefs is not for storing game state. PlayerPrefs用于存储游戏状态。 It is explicitly named "PlayerPrefs" to indicate its use: storing player preferences.它被明确命名为“PlayerPrefs”以表明其用途:存储玩家偏好。 It is essentially plain text.它本质上是纯文本。 It can easily be located, opened, and edited by any player.任何玩家都可以轻松定位、打开和编辑它。 This may not be a concern for all developers, but it will matter to many whose games are competitive.这可能不是所有开发人员都关心的问题,但对于许多具有竞争力的游戏来说却很重要。

Use PlayerPrefs for Options menu settings like volume sliders and graphics settings: things where you don't care that the player can set and change them at will.将 PlayerPrefs 用于选项菜单设置,如音量滑块和图形设置:您不关心播放器可以随意设置和更改它们的东西。

Use I/O and serialization for saving game data, or send it to a server as Json.使用 I/O 和序列化来保存游戏数据,或将其作为 Json 发送到服务器。 These methods are more secure than PlayerPrefs, even if you encrypt the data before saving.这些方法比 PlayerPrefs 更安全,即使您在保存前对数据进行了加密。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM