El objetivo#
Guardar el estado de la partida (nivel, vida, posición, ítems) en un archivo y volver a cargarlo. El enfoque estándar: convertir los datos a JSON (texto) y escribirlos en un archivo.
Paso 1 — Una clase de datos serializable#
Mete lo que quieras guardar en una clase marcada con [System.Serializable]:
[System.Serializable]
public class DatosPartida
{
public int nivel;
public float vida;
public Vector3 posicion;
public int monedas;
public string[] itemsRecogidos;
}Paso 2 — Guardar y cargar#
using System.IO;
using UnityEngine;
public class SistemaGuardado : MonoBehaviour
{
// Ruta segura y válida en cualquier plataforma
private string Ruta => Path.Combine(Application.persistentDataPath, "partida.json");
public void Guardar(DatosPartida datos)
{
string json = JsonUtility.ToJson(datos, true); // true = con formato legible
File.WriteAllText(Ruta, json);
Debug.Log("Guardado en: " + Ruta);
}
public DatosPartida Cargar()
{
if (!File.Exists(Ruta))
{
Debug.Log("No hay partida guardada");
return null;
}
string json = File.ReadAllText(Ruta);
return JsonUtility.FromJson<DatosPartida>(json);
}
public bool HayPartida() => File.Exists(Ruta);
public void Borrar() { if (File.Exists(Ruta)) File.Delete(Ruta); }
}Paso 3 — Usarlo#
var sistema = GetComponent<SistemaGuardado>();
// Guardar
var datos = new DatosPartida {
nivel = 3, vida = 80f,
posicion = transform.position,
monedas = 150,
itemsRecogidos = new[] { "llave", "espada" }
};
sistema.Guardar(datos);
// Cargar
DatosPartida cargado = sistema.Cargar();
if (cargado != null)
transform.position = cargado.posicion;Por qué persistentDataPath#
Application.persistentDataPath apunta a una carpeta con permisos de escritura en cada plataforma (Windows, macOS, Android, iOS). Si intentas escribir dentro de la carpeta de la build o en Application.dataPath, en móvil o en una build instalada no tienes permiso y falla. persistentDataPath es la respuesta correcta siempre.
Las trampas de JsonUtility#
JsonUtility viene de serie y es eficiente, pero tiene límites que sorprenden:
- No serializa
Dictionary(ni tipos genéricos sueltos): envuélvelos en listas o usa listas de pares. - Solo serializa lo que Unity serializa: campos públicos o
[SerializeField], no propiedades. - No maneja polimorfismo (guardar una lista de una clase base con objetos de derivadas).
Para casos complejos (diccionarios, polimorfismo), usa una librería como Newtonsoft Json.NET (disponible como paquete de Unity).
Buenas prácticas#
- Versiona tus datos: añade un campo
versionpara poder migrar guardados antiguos cuando cambies la estructura. - Guarda en momentos concretos (checkpoints, salir del nivel), no cada frame.
- Para varias ranuras de guardado, usa nombres de archivo distintos (
partida1.json,partida2.json). - Si te preocupa la manipulación, puedes cifrar el contenido, aunque para single-player rara vez merece la pena.
Errores frecuentes#
- Guardar en
dataPathen vez depersistentDataPath(falla en build/móvil). - Esperar que
JsonUtilityserialice unDictionary(no lo hace). - Olvidar
[System.Serializable]en la clase de datos (sale vacío). - No comprobar
File.Existsantes de cargar (excepción si no hay guardado). - Guardar componentes de Unity directamente (guarda datos simples: números, strings, Vector3, no GameObjects).
Resumen#
Mete los datos en una clase [System.Serializable], conviértela con JsonUtility.ToJson y escríbela con File.WriteAllText en Application.persistentDataPath. Carga con File.ReadAllText + JsonUtility.FromJson. Recuerda las limitaciones de JsonUtility (nada de Dictionary ni polimorfismo; para eso, Json.NET), versiona tus datos y no uses PlayerPrefs para esto.