Manage save games in Unity without pain using NDatabase

As developers, we all know that save games are an important aspect to most of our games, even more so on mobile where the game can be disrupted at any time.

But how could we solve this issue? How can we store our game data in a performant and easy way without the major limitations that Unitys PlayerPrefs put on our titles?
Today, I want to introduce you to NDatabase!
NDatabase is an easy to use data storage solution.
It is written purely in C# without native code which allows you to use it on all Unitys platforms that support file operations.


Before we get started, we need to ensure that we have everything ready.
For simplicity we will use the precompiled library.
You can download the ‘Download for .net 3.5′ version from https://ndatabase.codeplex.com.
After you downloaded it, extract the files content and move it into your projects Assets/Plugins folder.
Thats already all you need to start saving and loading your game data.

Define what you want to store

For this introduction, we will store data for an endless runner:

  • the current player position
  • the current score
  • the remaining lives
  • the inventory (a list of names)

We will store this information in a simple, lightweight data object that we can easily pass around as required.
As MonoBehavior and GameObject are very heavy objects, we will not attempt to store them and instead store what we actually need.

The classes

NDatabase makes it very easy to store data. All it requires is classes with data to store and it will handle it invisibly.
This is for example the case for object, float, int, string, bool or arrays, lists and dictionaries of these. In this example, we want to store an array of floats for the position, two ints for score and lives as well as a list of strings for the inventory. If we put this all together, this results in the following class:

public class PlayerData {
    public float[] PositionData;
    public int Score;
    public int Lives;
    public List Inventory = new List ();

    /// Gets or sets the position array
    /// The position.
    public Vector3 Position {
        get {
            if (PositionData == null) {
                return Vector3.zero;
            Vector3 result;
            for (int i = 0; i < 3; i++) {
                result [i] = PositionData [i];
            return result;
            PositionData = new float[3]{ value.x, value.y, value.z };

To focus on the specific NDatabase operations in the remaining post, I also want to provide the sources of the Player class you can find in the download. The Player class manages the PlayerData including the loading and saving of the data.

public class Player : MonoBehaviour
    private string _savegamePath;
    private IOdb _dataStorage;
    public PlayerData Data { get; private set; }

    private void Awake ()
        _savegamePath = Application.persistentDataPath + "/savegame";
        LoadFirstSavegame ();

    private void LoadFirstSavegame ()
        _dataStorage = OdbFactory.Open (_savegamePath);

        Data = _dataStorage.QueryAndExecute ().GetFirst ();
        if (Data == null) {
            Data = new PlayerData ();
        } else {
            transform.position = Data.Position;

    private void OnApplicationPause (bool pause)
        if (pause) {
            SavePlayerData ();
        } else {
            if (_dataStorage == null || _dataStorage.IsClosed ()) {
                LoadFirstSavegame ();

    private void OnApplicationQuit ()
        if (_dataStorage != null) {
            SavePlayerData ();
            _dataStorage.Dispose ();

    private void SavePlayerData ()
        Data.Position = transform.position;
        _dataStorage.Store (Data);
        _dataStorage.Commit ();

Creating the data storage

NDatabase makes creating the data storage as simple as it can be. All you need is a single line of code:

IOdb _dataStorage = OdbFactory.Open (savegamePath);

If you keep this reference stored on your player or a global manager, you can access the data at any time.

Saving your game the easy way

To prepare a new object to be stored, all we need to do is call

_dataStorage.Store (theObject);

This though will not yet write the data to disk. We need to finalize the changes to have them written to disk calling


In our case, we would like to update the existing save game instead of creating more and more entries in the database. NDatabase makes this very easy, you simply need to store a previously fetched object. As we assigned the latest player data to Data in the call to *LoadFirstSavegame*, all we need to do is update above Store call to

_dataStorage.Store (Data);

Loading the game made simple

Loading your save game is straight forward.
To get all stored PlayerData object, the simplest way is to call

var result = _dataStorage.QueryAndExecute ();

For the start, we will focus on getting the first PlayerData object, assuming there will only be one at any time.
This is done through the call to GetFirst:

Data = result.GetFirst();

Using Data, you can now restore the players position, score, lives and inventory by reassigning them to the appropriate objects.


With the knowledge we now have we can already handle complex save game data sets through the ease of NDatabase. But we are still limited to the amount of data we can store in realtime as writing a lot of data takes time.
In the next article of this series, we will present you with different strategies to challenge the current limits in realtime save game sizes.
We will continue our coverage on NDatabase in future blog posts, covering NDatabase more indepth, showing you how to optimize your queries beyond the simple call, use indices to accelerate accesses even further and leveraging the In Memory database capabilities of NDatabase to manage large, complex data sets during runtime.

Also published on Medium.

Exit mobile version