Saving and loading
How to trigger the saving and loading.
The GameSaver
component is responsible for moving data in and out of files, but it's the PersistenObjects
that actually read and set the variables to the appropriate values. How do you get them to do it when you want?
Automatically
The easiest option is to let Persyst handle this for you. By default, all PersistentObjects
will have both automaticallySave
and automaticallyLoad
set to true
(although you can change this in the inspector). When this is the case, the PersistentObject
will be listening to events sent by the GameSaver
component. These events are invoked when you call readFile()
or writeFile()
.
When calling readFile()
, all PersistentObjects
will retrieve their own data from the GameSaver
and update the relevant values. When calling writeFile()
, the PersistentObjects
will send their information to the GameSaver
so it can be serialiazed.
While this is super easy (you literally have to do nothing), there are many situations where you might want to control the order in which objects are loaded.
If using additive scene loading, fully automatic behaviour is not advised. The objects that are loaded "late" (object whose scene is not loaded when you call readFile()
) will retrieve their data correctly, but objects that are unloaded when you call writeFile()
will not write their data (as they do not exist at that time, and thus cannot receive the event). See the Custom Events section for a way to handle this while maintaining mostly-automatic behaviour.
Not automatically
This automatic behaviour can be disabled in two ways: changing the automaticallySave
and automaticallyLoad
variables in each individual PersistentObject
, or passing an optional bool
parameter to the readFile()
and writeFile()
functions, which makes them not fire the events.
The first method can be used to single out specific PersistentObjects
that have particular needs (for example, something that needs to be loaded after everything else), while the second method lets you handle the loading of everything yourself.
If you decide to do things yourself, there is even more control you can have over the granularity of the loading and saving processes.
By hand
You can call the LoadObject()
and SaveObject()
methods of a single PersistentObject
manually.
//no parameters or anything:
//these methods use reflection find everything tagged as saveable in this gameobject
GetComponent<PersistentObject>().LoadObject();
GetComponent<PersistentObject>().SaveObject();
With custom events
You can create your own events to trigger the saving and loading. This is a very powerful option, as you can make all objects of a certain type load at the same time, but still after all objects of another:
public class MyManagerClass{
public static System.EventHandler loadPlayer;
public static System.EventHandler loadEnemies;
void Start(){
//you can be sure the player will load first, and then all the enemies
//but oyu don't need to get a reference to each enemy to call their LoadObject()
loadPlayer?.Invoke();
loadEnemies?.Invoke()
}
}
public class Enemy{
//OnEnable because this should run before the event is called!
void OnEnable(){
GetComponent<PersistentObject>().registerCustomLoadEvent(MyManagerClass.loadEnemies);
}
}
This method is also particularly important if you have multiple scenes that are loaded and unloaded additively. If you remove a scene, all PersistentObjects
that live in it become no longer able to listen to the GameSaver
events. As such, if you want their data to be serialized you would need to save it just before the scene is unloaded. For example, something like this:
public class MySceneManager{
//instead of making this static,
//it might be a good idea to have an instance of this class in each scene, and use a tag to find it
//that way, only objects in that particular scene receive the event
public static System.EventHandler BeforeSceneUnloaded;
void unloadScene(){
BeforeSceneUnloaded?.Invoke();
UnityEngine.SceneManager.UnloadSceneAsync("scene");
}
}