What can I do with Save/Load addon?
- Save/load the state of the database to/from byte array during runtime on players devices
- Define which table and which fields you want to be saved/loaded using merge settings (it won't work if you skip this step)
- Create C# class for fine-grained row-level control.
- You can save the whole database without Save/Load addon (click)
Should I use additional save/load solutions besides this addon?
It's perfectly fine to use BGDatabase as the only solution for saving/loading, but it's a safer option to use a professional Save/Load solution and save database content as a part of all saved data (as a byte array field), even if you do not have any other data to save at the moment.Restrictions
Data inside partitions, created with partitioning addon can not be saved/loaded properly
How can I enable it?
ChooseAddons->Save Load
, click "Enable", mark tables/fields for saving/loading and save the database.
Examples
The following projects use SaveLoad addon to save/load runtime data:
- Basic 3D example
- Basic 2D example
- Inventory example
- Localization example (localization addon is required)
How it works?
SaveLoad addon can create multiple, configurable snapshots of database data and it can restore database state from these snapshots. Snapshots are constructed from the data, you marked as changeable, using Merge settings, described below. (Why it works like this?) Please, make sure you marked all data you want to be saved.
[Important] Loading with SaveLoad addon results in whole database to be reloaded, so if you use SaveLoad addon do not store references to tables/fields/entities, cause they will be obsolete after loading.
Why it works like this?
Here is the quick explanation and main reasons why it works the way it works and why it's much better then simply saving the whole database.
- we want to be able to change database between public game releases
- we want to make sure the old saves, which are made with old version of database will remain compatible with new database
- As a result of loading old save, we want to keep all saved data and we want to keep the changes we made to default database (shipped with a game)
Here is the detailed illustration about what we want to achieve:
Another diagram:
- How to change your database and keep it compatible with old save files- read the section below
- How to implement row-level control over merging- read Row-level controller section below.
How to use it?
//to save
byte[] bytes = BGRepo.I.Addons.Get<BGAddonSaveLoad>().Save();
//to load
BGRepo.I.Addons.Get<BGAddonSaveLoad>().Load(bytes);
Once you obtained bytes array using Save method, you need to write it to some persistent storage (file or cloud drive) See this minimalistic example for more details. There are also custom units/actions for Visual Scripting tools are available (see the full list at the bottom of this page)
Is it possible to save/load the whole database?
// !!!! This is not recommended !!!!
//to save
byte[] bytes = BGRepo.I.Save();
//to load
BGRepo.I.Load(bytes);
Using Merge settings
Using Merge settings you can choose which data to save/load. You want as less data to be saved as possible. You do not need to include data, which is "static" and can not be changed during play session.
There are only 2 rules for using merge settings and condition for choosing one or another is whether or not rows can be added or removed at runtime.
Rule #1 | Rule #2 | |
---|---|---|
Condition | Rows can NOT be added/deleted at runtime | Rows can be added/deleted at runtime |
How to apply | Turn on "Update matching". Optionally turn "Chose fields to update" on and mark the fields you want to be saved. (Tip: do not mark the fields which can not be changed at runtime) | Turn on "Add missing", "Remove Orphaned", "Update matching". |
Example | We use our Basic 3D project as an example | |
Rows from Player table can not be added or removed, so we do not turn on "Add missing" and "Remove Orphaned".
There is no need to save a name field, so we do not include it into settings
|
Rows from Collectable table can be added and removed, so we turn on "Add missing", "Remove Orphaned", "Update matching"
|
|
Screenshot for example |
How to change your database and keep it compatible with old save files?
- Do not include static data (the data, which can not be changed during game session) to save/load merge settings
- While developing new, public version of your game- you can change anything in your static data with one simple exception: you can not delete rows which may be referenced from saved data. Well, actually you can, but in this case you need to make sure your code should handle null relation values properly.(and they will be null if you remove the rows)
- As for the data, which is saved within database snapshot (save file),
- if you mark the field for saving using merge settings ("Update Matching") it will be loaded if the target row still exists in the default database
- if you mark the rows for saving ("Add missing" and "Remove Orphaned") the whole table will be updated with values from the saved file during loading, if this table (meta) still exists in the default database.
- if you want fine-grained, row-level control over loading - you can do it with C# controller class (read the next section for more details).
Here is an example from our basic 3D example project:
Row-level control over loading
Merge settings give you control over which tables/fields you want to save. But what about particular rows? What if you want to update/add/delete only those rows which comply to some condition?
This can be done with C# controller class, which can cancel update operation before it actually takes place. Currently there is no any merging on saving data, only on loading (but we reserved "merging on saving" option for future releases)
Here is a quick example of custom controller class. This controller is made for our basic 3D example project.
The main objective of this example controller is to prevent deleting Collectable rows for added scenes (scenes which was added between public game releases)
Here is the code
using BansheeGz.BGDatabase;
// Warning!!! this controller is implemented without code generation
//and may contains ugly boilerplate code
public class MyCustomSaveLoadController : BGMergeSettingsEntity.IMergeReceiver,BGMergeSettingsEntity.IRemoveOrphanedReceiver
{
private bool loading;
public bool OnBeforeMerge(BGRepo from, BGRepo to)
{
//here we want to know, if it's loading or saving?
//... Note (currently there is no merging on saving, but it can be implemented in future releases)
loading = to == BGRepo.I;
return false;
}
public void OnAfterMerge(BGRepo from, BGRepo to)
{
//no need to write anything here
}
//this callback is invoked before row removal. Return true to cancel this operation
public bool OnBeforeDelete(BGEntity toEntity)
{
if (loading)
{
if ("Collectable".Equals(toEntity.Meta.Name))
{
//before deleting Collectable row, we want to make sure- it's not connected to newly added scene
var scene = toEntity.Get<BGEntity>("Scene");
//if scene's version more than 0 - cancel removing by returning true
return scene != null && scene.Get<int>("version") > 0;
}
}
return false;
}
}
To see this controller in action:
- Download default 3D example project here
- Run the scene (Assets\BGDatabaseBasic3DExample\Scenes\BGExample1.unity), collect some objects and save the game
- Let's assume this was made by a real player with our game version 1, and now we want to develop version 1.1 and we want to add a new scene. We could add a new scene without any controller and without any issue, but we want to populate this scene with Collectable rows, and we do not want them to be deleted while loading save file. Without this controller, all Collectable rows will be overriden with the data from the save file, cause in SaveLoad merge settings we marked this table for "addingMissing" and "removingOrphaned" (which basically mean the whole table will be restored from save file)
- At this point we start to develop game version 1.1. We will omit real scene creating, cause all we want- is to make sure our save file will be loaded properly and it will not erase our newly added collectables for new scene
- Open BGDatabase window, choose
Configuration->Scene
and add new int field, call it "version" - Then choose
Database->Scene
and add a new scene - For newly created scene assign "version" field to 1
- Choose "Collectable" field for new scene and add some Collectables
- Now choose
Addons->SaveLoad
, and assign "Controller Type" field to MyCustomSaveLoadController (this is a class name of our controller) - Save the database and run BGExample1.unity scene again
- Load the game, choose
Database->Scene
and make sure all saved Collectables remain intact, just like newly added Collectables for our new scene. Also, you can run the same example without controller assigned to see the difference
- row-level controller on export/import page
BeforeSave event
You can declare you MonoBehavior script to be a receiver of BeforeSave event, so it could update the database before saving (code from the example project)
//this class implement BGAddonSaveLoad.BeforeSaveReciever interface,
//so OnBeforeSave method will be called before saving
public class BGPlayer : BGM_Player, BGAddonSaveLoad.BeforeSaveReciever
{
...
//this method is called before saving
void BGAddonSaveLoad.BeforeSaveReciever.OnBeforeSave()
{
//save current position , rotation and scene to the database
m_position = transform.position;
m_rotation = transform.rotation;
m_scene = BGE_Scene.FindEntity(scene => string.Equals(scene.Name, SceneManager.GetActiveScene().name));
}
}
Note, callback method is called on all loaded objects, including inactive game objects and prefabs.
Use the following code (at the beginning of your callback function) to filter out inactive game objects and prefabs.
// use the following line to filter out components, attached to inactive GameObjects
if (!gameObject.activeInHierarchy) return;
// use the following line to filter out prefabs
if (!gameObject.scene.IsValid()) return;
Additional downloads
- Custom actions for Playmaker to support SaveLoad addon
- Custom units for Bolt to support SaveLoad addon
- Custom nodes for Flow Canvas
- Node Canvas integration package has custom actions to use with SaveLoad addon
- Behavior Designer integration package has custom actions to use with SaveLoad addon
- UNode integration package has custom nodes to use with SaveLoad addon
- Game Creator integration package has custom actions to use with SaveLoad addon