Sistema de guardado
MCE usa un sistema de guardado basado en JSON con partidas con nombre, escrituras atómicas, rotación de autoguardados, versionado y extensibilidad. El sistema de guardado conserva todo el estado de juego del jugador: equipo, inventario, posición, medallas, progreso de misiones y variables de juego.

Tres de los interruptores de esa pantalla pertenecen a este sistema: Partida guardada elige el formato de serialización, Autoguard. historia decide si los hitos de la historia se guardan solos, y Subir a la nube es el que pregunta antes de salir de la máquina.
Arquitectura
El sistema de guardado se compone de varios componentes:
| Componente | Propósito |
|---|---|
SavegameManager | Orquesta las operaciones de guardar/cargar (un ScriptableObject enlazado mediante el SavegameInstaller) |
SavableObject | Clase base (un ScriptableObject) para cualquier objeto que conserve datos: SaveToText, LoadFromText y ResetSave |
MCESavesSerializer | Serializador JSON usado para todo texto guardado |
GameVariables | Almacén de variables con nombre: booleanos, enteros, flotantes y cadenas |
GameVariableHolder | Un SavableObject que posee un almacén GameVariables (GlobalGameData y cada SceneInfoAsset son titulares) |
GameVariableReference | Referencia de Inspector a una variable, usada por los nodos de CommandGraph |
Migrations/ | ISaveMigration, SaveMigrationPipeline y SaveFormatVersions: el versionado de guardados |
Qué se guarda
SavegameManager.SavegameObjects es la lista de objetos guardables que entran en cada guardado. Los propios del motor son:
| Objeto | Contiene |
|---|---|
GlobalGameData (guardado como SavableGlobalGameData) | Posición del jugador, dinero, medallas obtenidas, variables de juego, la marca de versión del formato, base secreta y gimnasio del jugador |
Roster | Los monstruos del equipo con todo su estado (especie, nivel, IV, EV, naturaleza, movimientos, PP, HP, estado, amistad...) |
MonsterStorage | Monstruos en las cajas |
Bag | Objetos y cantidades |
Dex | Seguimiento de vistos/capturados por especie |
QuestManager | Misiones activas, objetivos completados, recompensas reclamadas |
CharacterData | El personaje jugador |
TimeManager | La hora del día |
Nursery | La guardería de cría |
SceneInfoAsset | Las variables de juego propias de cada mapa |
Archivos de guardado
Un guardado es una carpeta, no un único archivo. Los guardados viven en Saves/ dentro del Application.persistentDataPath de Unity:
| Plataforma | persistentDataPath |
|---|---|
| Windows | %USERPROFILE%/AppData/LocalLow/<CompanyName>/<ProductName>/ |
| macOS | ~/Library/Application Support/<CompanyName>/<ProductName>/ |
| Linux | ~/.config/unity3d/<CompanyName>/<ProductName>/ |
| Android | Almacenamiento interno (privado de la app) |
| iOS | Sandbox de la app |
<persistentDataPath>/Saves/
2026-10-10-18-30-02/ a manual save, named with the current date
GlobalGameData.sav one .sav per savable object
Roster.sav
...
Manifest.txt the list of files a COMPLETE save must contain
Autosave/
OlderAutosave/
Los guardados se escriben para ser seguros ante cierres inesperados:
- Cada archivo se escribe de forma atómica (en un archivo temporal que luego lo reemplaza).
- Todo el guardado se escribe primero en una carpeta
<name>.stagingy solo se intercambia a su lugar después de escribir todos los archivos y el manifiesto. Un guardado interrumpido nunca deja una mezcla a medias de estado antiguo y nuevo. - Al cargar, si falta un archivo listado en el manifiesto, el guardado se interrumpió, así que se rechaza en lugar de cargar un guardado parcial.
Guardar y cargar
El jugador guarda desde el menú de pausa (la entrada Exit ofrece guardar, y la tecla de guardado del menú pregunta y escribe un guardado con el nombre de la fecha actual) y carga desde el menú principal (Continue carga el último guardado, Load los lista todos). Cuando el juego funciona en línea (MCE Online), el servidor conserva el progreso y no se escribe el guardado local.
Desde código, mediante el SavegameManager (ediciones Source y Studio):
// A new save named with the current date
yield return savegameManager.SaveGameWithCurrentDate(playerCharacter);
// Rotate the autosave
yield return savegameManager.Autosave(playerCharacter);
// Load by name, or the most recent one
yield return savegameManager.LoadGame(saveName);
yield return savegameManager.LoadLastSavegame();
SavegameManager.GetAllSavegames(out list) y AreThereSavegames() te dicen qué hay en disco. Los estados de prueba (SaveStates) viven junto a los guardados del jugador con su propio prefijo y están ocultos en todas las listas que puede ver el jugador.
engine.SaveSystem (ISaveSystem) expone Save(slot), Load(slot), HasSaveData(slot), DeleteSave(slot), OnSaved y OnLoaded, pero en el motor actual no está conectada al SavegameManager: nada atiende sus peticiones de guardar y cargar, y HasSaveData busca en persistentDataPath un archivo slot{N}.sav que el motor no escribe. Usa el SavegameManager (ediciones Source y Studio) o los menús del juego para los guardados reales. Consulta ISaveSystem.
Autoguardado
Dos opciones de la pantalla de Opciones (guardadas en el GameplayConfigurationFile) controlan el autoguardado:
| Opción | Cuándo guarda |
|---|---|
AutoSaveOnTeleporting | Cada vez que el jugador se teletransporta a otro mapa (no mientras se carga una partida) |
AutoSaveOnStory | En los puntos de la historia donde un grafo tiene un nodo AutoSaveIfEnabled |
Cada autoguardado va a Autosave/; el anterior se rota a OlderAutosave/ solo después de que el nuevo se haya escrito correctamente, de modo que un autoguardado interrumpido nunca destruye el último bueno.
- Coloca AutoSaveIfEnabled después del hito de la historia, nunca en medio de una batalla o una escena.
- Muestra un indicador de autoguardado al guardar.
Versionado de guardados
Los guardados llevan una versión de formato, que se escribe en SavableGlobalGameData al guardar y que el SaveMigrationPipeline compara al cargar, ejecutando las migraciones que necesita un guardado más antiguo:
| Versión | Nombre | Significado |
|---|---|---|
| 0 | LegacyHashes | Guardados anteriores a la marca de versión: especies, formas, habilidades, movimientos y objetos identificados por el hash en tiempo de ejecución de los nombres de sus assets |
| 1 | StableNames | Los identifica por los nombres de sus assets (cadenas estables). Es la versión actual |
Se agrega una nueva ISaveMigration al pipeline cuando cambia el formato, y se incrementa SaveFormatVersions.Current.
Nunca renombres campos serializados en clases relacionadas con el guardado sin proporcionar una migración, y trata los nombres de asset de especies, movimientos, habilidades y objetos como claves de guardado: renombrar un asset rompe los guardados que lo nombran. Si renombras un campo, los archivos de guardado existentes pierden esos datos al cargar; usa [FormerlySerializedAs("oldName")] para renombrar campos.
GameVariables
El sistema GameVariables es el almacén de MCE de valores con nombre persistentes. Lo leen y escriben los nodos de CommandGraph y los scripts, y se guarda con el objeto que lo posee.
Tipos de variable
| Tipo | Descripción | Ejemplo |
|---|---|---|
| Boolean | Banderas verdadero/falso | defeatedBrock = true |
| Integer | Contadores numéricos | fishCaught = 42 |
| Float | Números decimales | rivalTrust = 0.5 |
| String | Valores de texto | rivalName = "Gary" |
Cada variable vive en un titular: el global (GlobalGameData) o el SceneInfoAsset de un mapa. Una variable definida en un mapa es local a ese mapa; una global la comparte todo el juego.
Uso en CommandGraph
- SetBoolVariable, SetIntVariable: establecen el valor de una variable.
- IfBool, IfIntEquals, IfIntCompare, SwitchIntVariable: ramifican según una variable.
Cada uno de estos nodos tiene una referencia de variable donde eliges el titular, el tipo y el nombre.
Uso en código
// globalGameData is the injected GlobalGameData (a GameVariableHolder);
// the class that receives it implements IPlayerDataReceiver
bool beatBrock = globalGameData.GetVariable<bool>("defeatedBrock");
int fishCount = globalGameData.GetVariable<int>("fishCaught");
globalGameData.SetVariable("defeatedBrock", true);
globalGameData.SetVariable("fishCaught", fishCount + 1);
Datos de guardado personalizados
Usa GameVariables (todas las ediciones)
La forma más sencilla de conservar tus propias banderas y contadores con el guardado es almacenarlos como variables de juego en GlobalGameData: se guardan, cargan, migran y reinician junto con todo lo demás.
Extender SavableObject (ediciones Source y Studio)
Para una estructura de datos completa, escribe un nuevo SavableObject y agrega un asset de él a la lista Savegame Objects del asset SavegameManager:
[CreateAssetMenu(menuName = "MyGame/Custom Save Data")]
public class CustomSaveData : SavableObject
{
[Serializable] private class Data { public int counter; public List<string> unlocked; }
private Data data = new() { unlocked = new List<string>() };
public override string SaveToText(ISerializer<string> serializer, PlayerCharacter playerCharacter) =>
serializer.To(data);
public override IEnumerator LoadFromText(ISerializer<string> serializer, string data,
MCESettings mceSettings, MonsterDatabaseInstance monsterDatabase)
{
this.data = serializer.From<Data>(data);
yield break;
}
public override void ResetSave() => data = new Data { unlocked = new List<string>() };
}
Se escribe como su propio archivo .sav en cada guardado y se lista en el manifiesto. ResetSave le da un estado limpio cuando empieza una partida nueva.
Formato del archivo de guardado
Todo archivo .sav es JSON plano, escrito por MCESavesSerializer, así que se puede inspeccionar al depurar. La forma exacta depende del objeto al que pertenece (los datos globales, el equipo y el resto tienen cada uno su propio archivo).
Aunque los archivos de guardado son JSON legible, editarlos manualmente puede causar problemas si introduces datos inválidos (p. ej., un movimiento que no existe en la base de datos). Usa herramientas del juego o comandos de depuración en su lugar.
Buenas prácticas
- Prueba los ciclos de guardar/cargar. Guarda, sal, vuelve a cargar y verifica que todo esté intacto.
- Usa GameVariables para las banderas de eventos. Persisten automáticamente y se integran con CommandGraph.
- Planifica la migración de guardados. Si cambias el formato en una actualización, agrega un paso de migración.
- No renombres los nombres de asset de especies, movimientos, habilidades u objetos una vez que los jugadores tengan guardados.
- Gestiona con elegancia los fallos de carga. Un guardado dañado o interrumpido se rechaza con un diálogo de error en lugar de cargarse parcialmente.