Unity3d + SQLite

Это будет практическое руководство по использованию SQLite в Unity3d. Я расскажу что это такое, для чего может потребоваться, как реализовать и порекомендую инструменты для удобной работы. Статья ориентирована на начальный и средний уровни. В конце Вас ждёт небольшой бонус. Всем заинтересованным добро пожаловать под кат.

Что это?

SQLite это легковесная, встраиваемая и абсолютно самодостаточная реляционная база данных. Она является самой распространённой SQL-базой данных, по миру насчитывается не менее 500 млн. установок, это по сравнению со 100 млн. деплоев остальных SQL-баз данных. Используется в таких проектах, как Mozilla Firefox, Chrome, Skype, Windows Phone 8, iOS, Android, Symbian и прочие, и прочие… И самое главное она бесплатна, с открытым исходным кодом, а также имеет 100% покрытие тестами. Обо всём этом вы можете узнать на официальном сайте SQLite и в постах на хабре.

Зачем?

Отмечу, что в нашем небольшом отделе почти у всех стоят разные ОС, и поскольку для нас важна взаимозаменяемость, мы старались выбирать кроссплатформенные решения.
Для нас SQLite был хорошим вариантом для хранения внешней информации — ресурсов, конфигурационных настроек, локализации. От внешних xml-подобных файлов мы отказались сразу, из-за проблем с переносом на разные устройства, раздутости и медленности. Целесообразность использования SQLite для Вашего проекта можно посмотреть здесь.

Как?

Для создания и редактирования БД наш выбор пал на SQLite Manager — это плагин для Firefox, он бесплатный, удобный и кроссплатформенный. Единственное, что мне не понравилось — это приходилось дописывать SQL запрос на создание таблицы для добавления внешних ключей, а также невозможность редактирования записей в составленном VIEW из нескольких таблиц. А в остальном всё очень наглядно. Можно даже экспериментировать с SQL запросами к вашей БД. На выходе вы получаете один файлик, расширение которого можете указать сами (.db, .sqlite, .bytes и пр.), но Unity понимает только «.bytes».

Ещё один важный момент. Размещение в БД бинарников по типу картинок, музыки, видео является плохой практикой. Грубо говоря, всё что весит более 100 kb должно находиться в локальной папке, а в БД вы прописываете только пути. Иначе скорость чтения бинарника из базы становится больше, чем его загрузка локально.

Пришло время теперь разместить это всё в нашем Unity проекте. Для использования SQLite качаем библиотеки sqlite.dll (для Win, iOS и MacOS) и sqlite.os (для Android). Размещаем библиотеки здесь — Assets/Plugins/sqlite.dll и Assets/Plugins/Android/sqlite.so. Ежели папочки Plugins нет, то создаём её. Созданную БД (назовём её к примеру db.bytes) кладём в папочку Assets/StreamingAssets (создаём, если отсутствует). В итоге после деплоя, наша база окажется здесь:

Win и Mac OS:
Application.dataPath + "/StreamingAssets/db.bytes"

iOS:
Application.dataPath + "/Raw/db.bytes"

а вот на Android будет упакована в apk файл приложения:
"jar:file://" + Application.dataPath + "!/assets/db.bytes".
Вам потребуется использовать WWW класс, чтобы загрузить базу, а затем скопировать в папку Application.persistentDataPath + "/".

Но эти директории обладают только правами для чтения (кроме Android'a), если же вам потребуется записать что-либо в базу, её необходимо переместить сюда:

Win и Mac OS:
Application.dataPath + "/db.bytes"

iOS:
string path = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("/"));
path = path.Substring(0, path.LastIndexOf("/")) + "/Documents/db.bytes";
или так
string path = Application.dataPath.Substring(0, Application.dataPath.length - 5);
path = path.Substring(0, path.LastIndexOf("/")) + "/Documents/db.bytes";
или так
string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)  + "/db.bytes";

Про Android написано выше.

Затем подключаем пакет System.IO и пишем
File.Copy(openPath, savePath);

для Android'a соответственно
WWW www = new WWW(openPath);
while(!www .isDone) {} // тут очень внимательно, используйте корутины
File.WriteAllBytes(savePath, www.bytes);

Ну и бинарники ресурсов: модели, видео, музыка и т. д. кладём в папочку Assets/Resources/. После билда они упаковываются в бинарник resources.assets. А в приложении мы загружаем их — Resources.Load(String path). Ну например, мы положили звук в папочку Assets/Resources/Sounds/sound1.mp3, в базе у нас такая запись Sounds/sound1.mp3, и в приложении это будет выглядеть так
AudioClip a = Resources.Load("Sounds/sound1.mp3") as AudioClip;

Теперь настало время считать данные из нашей базы данных. Не люблю я писать SQL запросы и парсить сырые данные в объекты, поэтому специально для SQLite мы нашли opensource проект SQLite-net ORM, это библиотека объёктно-реляционного отображения. Это технология, которая связывает базу данных с концепциями объектно-ориентированного программирования. Здесь есть поддержка Linq, например, можно сделать выборку:

public class Favorite 
{
	[PrimaryKey, AutoIncrement]
	public int Id { get; set; }
	public int UserId { get; set; }
	public string Url { get; set; }
}

public Favorite[] GetFavorites(SQLiteConnection c, int id)
{
	var q = from f in c.Table<Favorite>()
				where f.UserId == id
				select f;
	return q.ToArray();
}

или редактировать:

public void AddFavorite(SQLiteConnection c, string url, int id)
{
	var fav = new Favorite() 
	{
		UserId = id,
		Url = url
	};
    c.Insert(fav);
}

Но Linq поддерживается не в полном объёме, например, не поддерживается JOIN, поэтому для сложных запросов придётся всё-таки писать SQL код.

Где мой бонус?

Ну в принципе самое главное я написал. Теперь обещанный бонус. Предлагаю вам опробовать SQLite в своём проекте, для решения задачи локализации.
Имеем базу данных:
Диаграмма базы данных
Для просмотра, редактирования и создания мы разработали специальную тулзу для локализаторов, которую я тоже выкладываю (Adobe Air build для Win и Mac OS): cсылка. Дерзайте!

Спасибо за внимание, жду ваших отзывов.
Поделиться публикацией

Комментарии 21

    –5
    Кстати для мака неплохой клиент для SQLite — MesaSQLite
      +1
      20$ брат. Редакторов SQLite просто гигантское количество, можно даже использовать Microsoft Access или OpenOffice Base.
      0
      Еще советую посмотреть на Sqlite Studio в качестве редактора, довольно вменяемый функционал, open source and free.
        +3
        Все таки вопрос «Зачем?» для меня остался открытым.

        1) Локализация делается через Excel-xml файлы — тут тебе и редактор и проверка правописания, плюс отдавать на перевод один файл который откроется даже в Open Office.
        2) Настройки сохранять/загружать из xml или банального ini файла.

        То есть достаточно xml парсера для чтения и совсем ничего для записи.

        Как по мне это Overengineering.

          0
          Лучше через гугл-доки — тогда всегда актуальная версия документа у всех. Каждому переводчику можно назначить область, где он может править, тока свой язык, и всё. Плюс скрипт, который выдаёт xml нужного формата — у нас так сделано, очень удобно, рекомендую.
            0
            Была такая идея — но отказались из за того что переводчики иногда просили вставлять картинки в документ Excel (для передачи сохраняли как xls) а у гугла с этим были проблемы
              0
              Если вы переводите всё внутри компании, зачем? Почему нельзя просто сказать работникам, кто что должен переводить, а уж они сами разберутся? Зачем устанавливать тоталитаризм внутри фирмы? У вас там тысячи китайцев с улицы работают?

              Для синхронизации можно и Git использовать.
                0
                Левые переводчики работают, паблишерские. Чтобы случайно не закосячили чужое, а то потом концов не найдёшь.
              0
              Для локализации — это явно overengineering. Тем более учитывая, сколько уже готовых либ для Unity лежит в Asset Store.

              А так, sqlite может использоваться как база данных внутри игрушки(если там сложная логика и нужно хранить кучу данных). Проверено, работает шустро.
                0
                SQLite для локализации рабочее решение. Такое применение вы можете встретить среди iOS разработчиков (и для ресурсов тоже). Имея несколько xml файлов локализации, sqlite даёт всегда один. Даже если вы объедините всю локализацию в один xml файл, поиск в БД всегда будет быстрее. Многим людям удобнее работать с SQL ORM чем с XPath. Ну и вербозность xml уже достала.
                Возможно вам сейчас кажется имплементация SQLite большим оверхедом, но как только вы внедрите его в проект, вы поймёте насколько всё стало удобно, практично и быстро. Я конечно же не навязываю своё мнение, но SQLite в вашем приложении опрелённый плюс, пусть даже не для локализации, но всё же попробуете его.
                  +2
                  Зачем такие сложности?
                  На старте приложение вычитывает нужный язык (один раз пропарсить xml) и держится в Dictionary<key,text>
                  Время доступа почти мгновенное.
                    0
                    среди ios разработчиков есть встроенные штуки для локализации в виде разных файликов <ключ=значение>, которое конвертируется в хэшмап. на юнити такое же сделать — дело одного дня максимум, даже с эдитором для непрограммистов. если лень, то можно купить за 20 баксов. Это будет работать быстрее и проще, чем грузить еще одну либу.
                  +1
                  Хорошая статья! Только есть вопрос: в streaming assets файлы неизменяемые, то есть будет доступ только для чтения?
                    0
                    Да, совершенно верно, только для чтения.
                      0
                      Тогда незачем такие ухищрения, надо просто сериализацию хорошую сделать.

                      А еще можно держать шаблон вашей бд не в стриммингассетс, а в ресурсах. тогда при копировании в persistentDataPath не надо делать изврата c вычислением названия файла в зависимости от андроида, айфона.
                        0
                        1. Что вы имеете ввиду под сериализацией? Работу с сервером? Для нас важно было не забивать канал излишними инициализационными данными, любые изменения приходят дифом и пишутся в базу.

                        2. Просветите меня тогда, пожалуйста, как из ресурсов использовать базу sqlite только для чтения без копирования в пользовательскую директорию? Я вот не знаю как.
                          0
                          1. Базу данных надо прикручивать когда много данных, данные постоянные обновляются или в приложении сложная логика (использование xml, json, <что там еще> причиняет большое неудобство). В конкретном случае локализации можно обойтись простым Dictionary, создаваемым из файла. Такое решение проще написать, работать это будет быстрее.

                          2. Без копирования никак. Но вычисление по имени файла в зависимости от платформы, обрезание по имени файла — это непонятно, если потом надо будет поддерживать новые платформы, то надо будет искать это место, добавлять еще ifdef-ов.
                          Более независимым решением является копирование с ресурсов:

                          string fileName = Application.persistentDataPath + "/database.bytes";
                          if (!System.IO.File.Exists (fileName)) {
                          	var asset = (TextAsset)Resources.Load ("database");
                          	File.WriteAllBytes (fileName, asset.bytes);
                          }
                          
                            –1
                            1. В локализации много данных. Про преимущества над xml я уже рассказал. Взгляните на sqlite как на альтернативу, а не как на единственное правильное решение.

                            2. Нет, это не лучше. Потому что если рассматривать кейс только для чтения, вы будите удваивать данные, которые уже лежат в ресурсах. SteaningAssets позволяет избавиться от этого. Использования директив препроцессора для разработки кроссплатформенных приложений — это тоже нормально. И пользовательские директории приложения на разных платформах отличны, в статье я об этом упоминал.
                              0
                              1. Можете использовать json, он не раздут. Можете сделать, как сделано в локализации ios: ключ=значение. Еще проще и правильнее(если вы компания, а не инди разработчик) взять плагин за 50 баксов. Любое из этих решение будет удобнее, работать будет быстрее. Не надо придумывать альтернативу, которая заведомо хуже «стандартного» решения.

                              2. Если для чтения, то да. Но таких кейсов, когда бд используется только для чтения и обязательно нужно использовать бд очень мало, поэтому я ими и пренебрег.

                              >пользовательские директории приложения на разных платформах отличны
                              вот именно поэтому и хочется писать код, которому будет все равно на запускаемую платформу. Я положил файл в ресурсы и забыл. Я не парюсь, что на blackberry у меня не работает, я не буду искать это место в коде, когда надо будет портировать под win8.
                                0
                                Быстрее точно нет, «хуже стандартного решения» — субъективно. Объективно минусы: нет проверки орфографии и неудобный формат, первое решается плагином проверки орфографии, второе уже решено редактором. А преимущества над xml я уже назвал, их больше и они перекрывают минусы.
                      0
                      я тоже как-то не понял в чем радость базы данных в режиме «только чтение». В любом случае за несколько часов можно написать тул который будет экспортировать информацию в любом виде (в тот же xml) и не придется тащить еще один бинарный плагин. Идея прямо писать ресурсы в БД без проверок как-то вовсе пугает. Если мои переводчики через раз умудряются испортить текстовый файл, я даж не представляю что они сделают с БД через редактор. Да и не пошлешь подрядчикам бинарник sqlite…

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое