¿Cuál es la mejor manera de guardar el estado del juego?

Resuelto Sizzling asked hace 7 años • 2 respuestas

Encuentro la mejor manera de guardar datos del juego en el motor de juegos Unity3D.
Al principio, serializo objetos usando BinaryFormatter.

Pero escuché que esta forma tiene algunos problemas y no es adecuada para guardar.
Entonces, ¿cuál es la forma mejor o recomendada de guardar el estado del juego?

En mi caso, el formato para guardar debe ser una matriz de bytes.

Sizzling avatar Dec 05 '16 08:12 Sizzling
Aceptado

Pero escuché que esta manera tiene algunos problemas y no es adecuada para guardar.

Así es. En algunos dispositivos, hay problemas con BinaryFormatter. Empeora cuando actualizas o cambias la clase. Es posible que se pierdan sus configuraciones anteriores porque las clases ya no coinciden. A veces, obtienes una excepción al leer los datos guardados debido a esto.

Además, en iOS tienes que añadir Environment.SetEnvironmentVariable("MONO_REFLECTION_SERIALIZER", "yes");o tendrás problemas con BinaryFormatter.

La mejor forma de ahorrar es con PlayerPrefsy Json. Puedes aprender cómo hacerlo aquí .

En mi caso, el formato para guardar debe ser una matriz de bytes.

En este caso, puede convertirlo a json y luego convertir el json stringa byteuna matriz. Luego puede usar File.WriteAllBytesy File.ReadAllBytespara guardar y leer la matriz de bytes.

Aquí hay una clase genérica que se puede utilizar para guardar datos. Casi lo mismo que este pero no usa PlayerPrefs. Utiliza un archivo para guardar los datos json.

DataSaverclase:

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

USO :

Clase de ejemplo para guardar :

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

Guardar datos:

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

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

Cargar datos:

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]);
}

Borrar datos:

DataSaver.deleteData("players");
Programmer avatar Dec 05 '2016 02:12 Programmer

Sé que esta publicación es antigua, pero en caso de que otros usuarios también la encuentren mientras buscan estrategias de guardado, recuerda:

PlayerPrefs no sirve para almacenar el estado del juego. Se denomina explícitamente "PlayerPrefs" para indicar su uso: almacenar las preferencias del jugador. Es esencialmente texto sin formato. Cualquier jugador puede localizarlo, abrirlo y editarlo fácilmente. Puede que esto no sea una preocupación para todos los desarrolladores, pero sí será importante para muchos cuyos juegos son competitivos.

Utilice PlayerPrefs para la configuración del menú de opciones, como controles deslizantes de volumen y configuración de gráficos: cosas que no le importan y que el reproductor puede configurar y cambiar a voluntad.

Utilice E/S y serialización para guardar datos del juego o envíelos a un servidor como Json. Estos métodos son más seguros que PlayerPrefs, incluso si cifra los datos antes de guardarlos.

 avatar Feb 20 '2021 03:02