LiteDB and Unity

Tulenber 19 June, 2020 ⸱ Intermediate ⸱ 6 min ⸱ 2019.4.0f1 ⸱

Let’s take a look at the LiteDB - a noSql alternative for current gaming storage standards.

In the last ten years, noSql solutions are slowly seeping into the basic set of development tools. It is understandable since the capabilities provided by relational databases not needed so often, and the performance gained with migration to pure noSql is never excessive. In the article about choosing the data storage method for Unity, we concluded that the de facto industry standard is SQLite, which can be adapted not only for the relational use case but also in the form of a noSql solution. The pure noSql solutions are not so popular and are the brightest examples of open source projects. However, as noted earlier, if your soul is hungry for adventure, and the project does not claim to take over the world, then why not give it a try.

LiteDB

LiteDB is an embedded noSql database written in .NET, which makes it easy to use in projects on the latest Unity versions. It is a small project with a small bus factor, but it has everything you need to use as a full-fledged embedded database. Databases are not a big problem in games creating, so there are not so many popular solutions, and LiteDB is the most visible option among the SQLite alternatives. More precisely, it is at least somehow noticeable, and you can find at least some information on its application. For example, one such article is a blog post by Mark Hedberg, where he shares his personal experience with its use. The second one is from Andrey Podkolzin, in which he compares performance between LiteDb, SQLite, and flat files.

Documentation

Pretty concise documentation can be found on the official website.

Internal organization

The description of the database’s internal structure can be found in the corresponding section of the documentation - Data Structure.

Setup

Go to the package page on nuget.org, download the version you need from the link Download package, rename the extension of the downloaded file to .zip, unzip and copy the version of Dll suitable for your Scripting Backend to the project.

Also you need to create a file link.xml with those settings (thanks to TigerHix for his gist)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<linker>
    <assembly fullname="System">
        <type fullname="System.ComponentModel.TypeConverter" preserve="all"/>
        <type fullname="System.ComponentModel.ArrayConverter" preserve="all"/>
        <type fullname="System.ComponentModel.BaseNumberConverter" preserve="all"/>
        <type fullname="System.ComponentModel.BooleanConverter" preserve="all"/>
        <type fullname="System.ComponentModel.ByteConverter" preserve="all"/>
        <type fullname="System.ComponentModel.CharConverter" preserve="all"/>
        <type fullname="System.ComponentModel.CollectionConverter" preserve="all"/>
        <type fullname="System.ComponentModel.ComponentConverter" preserve="all"/>
        <type fullname="System.ComponentModel.CultureInfoConverter" preserve="all"/>
        <type fullname="System.ComponentModel.DateTimeConverter" preserve="all"/>
        <type fullname="System.ComponentModel.DecimalConverter" preserve="all"/>
        <type fullname="System.ComponentModel.DoubleConverter" preserve="all"/>
        <type fullname="System.ComponentModel.EnumConverter" preserve="all"/>
        <type fullname="System.ComponentModel.ExpandableObjectConverter" preserve="all"/>
        <type fullname="System.ComponentModel.Int16Converter" preserve="all"/>
        <type fullname="System.ComponentModel.Int32Converter" preserve="all"/>
        <type fullname="System.ComponentModel.Int64Converter" preserve="all"/>
        <type fullname="System.ComponentModel.NullableConverter" preserve="all"/>
        <type fullname="System.ComponentModel.SByteConverter" preserve="all"/>
        <type fullname="System.ComponentModel.SingleConverter" preserve="all"/>
        <type fullname="System.ComponentModel.StringConverter" preserve="all"/>
        <type fullname="System.ComponentModel.TimeSpanConverter" preserve="all"/>
        <type fullname="System.ComponentModel.UInt16Converter" preserve="all"/>
        <type fullname="System.ComponentModel.UInt32Converter" preserve="all"/>
        <type fullname="System.ComponentModel.UInt64Converter" preserve="all"/>
    </assembly>
    
    <!--https://docs.microsoft.com/en-us/dotnet/api/system.linq.expressions.lambdaexpression.name?view=netframework-4.7.2&viewFallbackFrom=netframework-2.0-->
    <assembly fullname="System.Core">
        <type fullname="System.Linq.Expressions" preserve="all"/>
        <type fullname="System.Linq.Expressions.*" preserve="all"/>
        <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all"/>
        <type fullname="System.Linq.Expressions.LambdaExpression" preserve="all"/>
    </assembly>
    <assembly fullname="System.Linq.Expressions">
        <type fullname="System.Linq.Expressions" preserve="all"/>
        <type fullname="System.Linq.Expressions.*" preserve="all"/>
        <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all"/>
        <type fullname="System.Linq.Expressions.LambdaExpression" preserve="all"/>
    </assembly>
    <assembly fullname="netstandard">
        <type fullname="System.Linq.Expressions" preserve="all"/>
        <type fullname="System.Linq.Expressions.*" preserve="all"/>
        <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all"/>
        <type fullname="System.Linq.Expressions.LambdaExpression" preserve="all"/>
    </assembly>
    <assembly fullname="LiteDB" preserve="all"/>
</linker>

Usage

Since the code of the real project is not very suitable for open demonstration, a minimal implementation was created that simulates actual application.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
using System.IO;
using System.Linq;
using LiteDB;
using UnityEngine;
using UnityEngine.UI;

// POCO Object for storage
public class GameData
{
    // Id needed to store object in database
    public int Id { get; set; }
    // Click counter
    public int Counter { get; set; }
}

// Database usage class
public class DatabaseManager : MonoBehaviour
{
    // Link to counter text
    public Text text = null;

    // Parameters of access to the database
    private string _connectionString;
    private string _databaseName;
    
    // Start is called before the first frame update
    void Start()
    {
        // Configure access settings
        _connectionString = "Filename=" + Path.Combine(Application.persistentDataPath, "database");
        _databaseName = "gameData";

        // Display initial data
        text.text = GetGameData().Counter.ToString();
    }

    // Click the counter increase button
    public void IncreaseClick()
    {
        // Get an object from the database
        GameData gameData = GetGameData();
        // Increment counter
        gameData.Counter += 1;
        // Save the object to the database
        StoreGameData(gameData);

        // Update the text through receiving the object (for testing purposes only)
        text.text = GetGameData().Counter.ToString();
    }

    // Click the cleanup database button
    public void ClearClick()
    {
        // Clear the database
        ClearData();

        // Update text
        text.text = GetGameData().Counter.ToString();
    }

    // Save the object to the database
    private void StoreGameData(GameData gameData)
    {
        // Connect to the database
        using(var db = new LiteDatabase(_connectionString))
        {
            // Get the collection (or create if it does not exist)
            var col = db.GetCollection<GameData>(_databaseName);

            // Add or update an object
            col.Upsert(gameData);
        }
    }

    // Get the object from the database
    private GameData GetGameData()
    {
        // Connect to the database
        using(var db = new LiteDatabase(_connectionString))
        {
            // Get the collection (or create if it does not exist)
            var col = db.GetCollection<GameData>(_databaseName);

            // Create a new object if it is not in the database
            if (col.Count() == 0)
            {
                GameData gameData = new GameData();
                gameData.Counter = 0;
                return gameData;
            }

            // Get the object from the database
            var result = col.FindAll();
            return result.First();
        }
    }

    // Clear the database
    private void ClearData()
    {
        // Connect to the database
        using(var db = new LiteDatabase(_connectionString))
        {
            // Get the collection (or create if it does not exist)
            var col = db.GetCollection<GameData>(_databaseName);

            // Clear Collection
            col.DeleteAll();
        }
    }
}

Result

It just works =)
Result

Disadvantage

The primary negative side of using this database is the lack of a tool for viewing its contents on platforms other than Windows, but you can solve it on our own.

Conclusion

The standard method for storing information for projects on Unity is SQLite. It is a simple database from which it is clear what to expect. Changing it for something fashionable without serious reason for that makes no sense. However, if you are starting a new project, then it is quite possible to consider LiteDB as an alternative. The experience of using this database in a personal project did not reveal any negative sides, so we can try to use it for more critical projects, which we will talk about in the future. See you next time! =)



Privacy policyCookie policyTerms of service
Tulenber 2020