Save Load

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)

How can I enable it?

Chose Addons->Save Load, click "Enable", mark tables/fields for saving/loading and save the database.

Examples

The following projects uses SaveLoad addon to save/load data:

  1. Basic 3D example
  2. Basic 2D example
  3. Inventory example
  4. 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.

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.

  1. we want to be able to change database between public game releases
  2. we want to make sure the old saves, which are made with old version of database will remain compatible with new database
  3. As a result of loading old save, we want to keep all saved data + we want to keep all data we made to default database (shipped with a game)

Here is the detailed illustration about what we want to achieve:

Another diagram:

Read more about:
  1. How to change your database and keep it compatible with old save files- read the section below
  2. How to implement row-level control over merging- read Row-level controller section below.

How to use?

//to save
byte[] bytes = BGRepo.I.Addons.Get<BGAddonSaveLoad>().Save();

//to load
BGRepo.I.Addons.Get<BGAddonSaveLoad>().Load(bytes);

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 chose 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?

  1. Do not include static data (the data, which can not be changed during game session) to save/load merge settings
  2. 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)
  3. 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, chose Configuration->Scene and add new int field, call it "version"
  • Then chose Database->Scene and add a new scene
  • For newly created scene assign "version" field to 1
  • Chose "Collectable" field for new scene and add some Collectables
  • Now chose 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, chose 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
Read more about:
  1. 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));
    }
}

Additional downloads

  1. Custom action for Playmaker to support SaveLoad addon is available for download here
  2. Custom unit for Bolt to support SaveLoad addon is available for download here