Auto save game on application close (synchronous)
While our built-in state manager doesn't provide synchronous (blocking) APIs to serialize state, there are cases when you may need the blocking versions, eg to automatically save the game when player forcefully closes the application (for example, closing application window instead of clicking "EXIT" button in the title menu).
Below is an example of a custom state manager (see the guide for more info on overriding engine services), which exposes sync versions to save both game and global state.
Code: Select all
using System;
using Naninovel;
using Naninovel.Commands;
[InitializeAtRuntime(int.MinValue, typeof(StateManager)), Goto.DontResetAttribute]
public class SyncSaveStateManager : StateManager
{
public SyncSaveStateManager (StateConfiguration config, EngineConfiguration engineConfig)
: base(config, engineConfig) { }
public void QuickSaveGame ()
{
var slotId = string.Format(Configuration.QuickSaveSlotMask, 1);
SaveGame(slotId);
}
public void SaveGame (string slotId)
{
var state = new GameStateMap();
state.SaveDateTime = DateTime.Now;
state.Thumbnail = Engine.GetService<ICameraManager>().CaptureThumbnail();
SaveAllServicesToState<IStatefulService<GameStateMap>, GameStateMap>(state);
PerformOnGameSerializeTasks(state);
state.RollbackStackJson = SerializeRollbackStack();
(GameSlotManager as PlayerPrefsGameStateSlotManager)?.Save(slotId, state);
SaveGlobal();
}
public void SaveGlobal ()
{
var state = new GlobalStateMap();
var slotId = Configuration.DefaultGlobalSlotId;
SaveAllServicesToState<IStatefulService<GlobalStateMap>, GlobalStateMap>(state);
(GlobalSlotManager as PlayerPrefsGlobalStateSlotManager)?.SaveAsync(slotId, state);
}
}
Notice, that the implementation assumes you've selected PlayerPrefs
game and global serialization handlers.
Below is an example script, which will automatically save the game to the first quick save slot when the application is closed.
Code: Select all
using System.Collections;
using Naninovel;
using UnityEngine;
public class AutoSaveGame : MonoBehaviour
{
private SyncSaveStateManager stateManager;
private ITitleUI titleUI;
[RuntimeInitializeOnLoadMethod]
private static void Initialize ()
{
var gameObject = new GameObject(nameof(AutoSaveGame));
gameObject.AddComponent<AutoSaveGame>();
DontDestroyOnLoad(gameObject);
}
private IEnumerator Start ()
{
while (!Engine.Initialized)
yield return null;
stateManager = Engine.GetService<SyncSaveStateManager>();
titleUI = Engine.GetService<IUIManager>().GetUI<ITitleUI>();
}
private void OnApplicationQuit ()
{
if (!titleUI?.Visible ?? true) // Don't save when on the title screen.
stateManager?.QuickSaveGame();
}
}
You can now load last auto-saved (quick) slot as follows:
Code: Select all
Engine.GetService<IStateManager>().QuickLoadAsync()
Below is an example of a custom component, which can be used to handle uGUI's button "OnClick" event to load the last auto save:
Code: Select all
using Naninovel;
using UnityEngine;
public class LoadLastQuickSave : MonoBehaviour
{
public void Load () => Engine.GetService<IStateManager>().QuickLoadAsync();
}
Attach that component to uGUI button and set the "OnClick" handler to LoadLastQuickSave.Load
: