LiteDB и Unity

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

Взглянем на LiteDB - noSql альтернативу для текущих стандартов хранения данных в игростроении.

Последние 10 лет noSql решения потихоньку просачиваются в базовый набор инструментов для разработки. Это и понятно, далеко не всегда мы нуждаемся в возможностях, предоставляемых реляционными базами данных, а прирост производительности при отказе от них никогда не бывает лишним. В статье о выборе метода хранения данных для Unity мы пришли к выводу, что промышленным стандартом де-факто является применение SQLite, который можно приспособить не только под реляционный вариант использования, но и в виде noSql решения. Альтернативы в виде чистых noSql решений не обладают такой популярностью и являются ярчайшими примерами проектов с открытым исходным кодом. Однако, как было отмечено ранее, если ваша душа жаждет приключений, а проект не претендует на захват мира, то почему бы не попробовать.

LiteDB

LiteDB - это встраиваемая noSql база данных написанная на .NET, что позволяет легко использовать её в проектах на последних версиях Unity. Это небольшой проект с маленьким автобусным фактором, однако он обладает всем необходимым для применения в качестве полноценной встроенной базы данных. Судя по всему, базы данных не являются большой проблемой в создании игр, поэтому можно выделить не так много проектов и LiteDB является самым заметным вариантом среди альтернатив SQLite, точнее, она хоть как-то заметна и по её применению можно найти хоть какую-то информацию. Например, одной из таких статей является запись в блоге Марка Ходберга(Mark Hedberg), в которой он делится личным опытом её применения. Вторая статья с нашего любимого хабра, в которой проводится сравнение производительности между LiteDb, SQLite и файлами.

Документация

Довольно лаконичную документацию можно найти на официальном сайте.

Внутреннее устройство

За описанием внутреннего устройства базы данных можно проследовать в соответствующий раздел документации Data Structure.

Установка

Сходить на страницу пакета на nuget.org, скачать нужную вам версию по ссылке Download package, переименовать расширение скачанного файла в .zip, распаковать и скопировать подходящую для вашего Scripting Backend версию Dll в проект.

Так же необходимо создать файл link.xml с таким содержанием(спасибо TigerHix за 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>

Использование

Так как код реального проекта не очень приспособлен для открытого демонстрирования, была создана минимальная реализация, имитирующая реальное применение.

  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 Объект для хранения данных
public class GameData
{
    // Id необходимый для хранения объекта в базе данных
    public int Id { get; set; }
    // Счётчик нажатий кнопки
    public int Counter { get; set; }
}

// Класс для работы с базой данных
public class DatabaseManager : MonoBehaviour
{
    // Ссылка на текст счётчика
    public Text text = null;

    // Параметры доступа к базе данных
    private string _connectionString;
    private string _databaseName;
    
    // Start is called before the first frame update
    void Start()
    {
        // Настройка параметров доступа
        _connectionString = "Filename=" + Path.Combine(Application.persistentDataPath, "database");
        _databaseName = "gameData";

        // Отображение первоначальных данных
        text.text = GetGameData().Counter.ToString();
    }

    // Клик кнопки увеличения счётчика
    public void IncreaseClick()
    {
        // Получение объекта из базы
        GameData gameData = GetGameData();
        // Увеличение счётчика
        gameData.Counter += 1;
        // Сохранение объекта в базу
        StoreGameData(gameData);

        // Обновление текста через получение объекта(исключительно в тестовых целях)
        text.text = GetGameData().Counter.ToString();
    }

    // Клик кнопки очистки базы данных
    public void ClearClick()
    {
        // Очистка базы данных
        ClearData();

        // Обновление текста
        text.text = GetGameData().Counter.ToString();
    }

    // Сохранение объекта в базу данных
    private void StoreGameData(GameData gameData)
    {
        // Соединение с базой данных
        using(var db = new LiteDatabase(_connectionString))
        {
            // Получение коллекции (или создание, если она не существует)
            var col = db.GetCollection<GameData>(_databaseName);

            // Добавление или обновление объекта
            col.Upsert(gameData);
        }
    }

    // Получение объекта из базы данных
    private GameData GetGameData()
    {
        // Соединение с базой данных
        using(var db = new LiteDatabase(_connectionString))
        {
            // Получение коллекции (или создание, если она не существует)
            var col = db.GetCollection<GameData>(_databaseName);

            // Создание нового объекта в случае его отсутствия в базе данных
            if (col.Count() == 0)
            {
                GameData gameData = new GameData();
                gameData.Counter = 0;
                return gameData;
            }

            // Получение объекта из базы данных
            var result = col.FindAll();
            return result.First();
        }
    }

    // Очистка базы данных
    private void ClearData()
    {
        // Соединение с базой данных
        using(var db = new LiteDatabase(_connectionString))
        {
            // Получение коллекции (или создание, если она не существует)
            var col = db.GetCollection<GameData>(_databaseName);

            // Очистка коллекции
            col.DeleteAll();
        }
    }
}

Результат

Оно просто работает =)
Result

Минусы

Основной отрицательной стороной использования этой базы данных является отсутствие инструмента для просмотра её содержимого на платформах отличных от Windows, но этот вопрос решаем собственными силами.

Заключение

Стандартным методом хранения информации для проектов на Unity является SQLite, это просто база, от которой понятно что ожидать. Менять её на что то новомодное без серьёзных на то оснований нет смысла. Однако если вы начинаете новый проект, то вполне можно рассмотреть в качестве альтернативы LiteDB. Опыт использования этой базы в личном проекте не выявил какого-то негатива, так что можно попробовать применить её для более серьёзных проектов, о чём можно будет рассказать в следующий раз. Пока! =)



Privacy policyCookie policyTerms of service
Tulenber 2020