¿Cuál es la mejor manera de guardar el estado del juego?
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.
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 PlayerPrefs
y 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 string
a byte
una matriz. Luego puede usar File.WriteAllBytes
y File.ReadAllBytes
para 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.
DataSaver
clase:
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");
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.