Хранение данных в базе данных, извините за тавтологию — это очень удобно. Сегодня мы рассмотрим как организовать подключение к встраиваемой БД SQLite в Unity. Это, на мой взгляд, гораздо лучше чем использовать PlayerPrefs. В примере используется Unity 5.3.1f1.
- Создаем БД любым доступным инструментом. Я использовал менеджер SQLite в виде плагина к Firefox. Файл БД надо сохранить в папке [путь_к_вашему_проекту_Unity]\Assets\StreamingAssets\. В примере используется имя файла db.bytes.
- Нам понадобятся библиотеки для доступа к SQLite. Скачать их можно отсюда. Скачиваем и распаковываем куда нибудь архив SQLite4Unity3d. Копируем папку Assets\Plugins\ из распакованного архива в [путь_к_вашему_проекту_Unity]\Assets\Plugins\.
- Теперь создаем в папке [путь_к_вашему_проекту_Unity]\Assets\Scripts\ файл DataService.cs:
using SQLite4Unity3d; using UnityEngine; #if !UNITY_EDITOR using System.Collections; using System.IO; #endif using System.Collections.Generic; public class DataService { public SQLiteConnection _connection; public DataService(string DatabaseName){ #if UNITY_EDITOR var dbPath = string.Format(@"Assets/StreamingAssets/{0}", DatabaseName); #else // check if file exists in Application.persistentDataPath var filepath = string.Format("{0}/{1}", Application.persistentDataPath, DatabaseName); if (!File.Exists(filepath)) { Debug.Log("Database not in Persistent path"); // if it doesn't -> // open StreamingAssets directory and load the db -> #if UNITY_ANDROID var loadDb = new WWW("jar:file://" + Application.dataPath + "!/assets/" + DatabaseName); // this is the path to your StreamingAssets in android while (!loadDb.isDone) { } // CAREFUL here, for safety reasons you shouldn't let this while loop unattended, place a timer and error check // then save to Application.persistentDataPath File.WriteAllBytes(filepath, loadDb.bytes); #elif UNITY_IOS var loadDb = Application.dataPath + "/Raw/" + DatabaseName; // this is the path to your StreamingAssets in iOS // then save to Application.persistentDataPath File.Copy(loadDb, filepath); #elif UNITY_WP8 var loadDb = Application.dataPath + "/StreamingAssets/" + DatabaseName; // this is the path to your StreamingAssets in iOS // then save to Application.persistentDataPath File.Copy(loadDb, filepath); #elif UNITY_WINRT var loadDb = Application.dataPath + "/StreamingAssets/" + DatabaseName; // this is the path to your StreamingAssets in iOS // then save to Application.persistentDataPath File.Copy(loadDb, filepath); #endif Debug.Log("Database written"); } var dbPath = filepath; #endif _connection = new SQLiteConnection(dbPath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create); Debug.Log("Final PATH: " + dbPath); } }
Этот код взят практически без изменений из проекта SQLite4Unity3d. Оттуда убраны лишние методы которые приведены там просто для примера. А так же переменная _connection сделана публичной. Как видно класс кроссплатформенный. Его можно использовать как под Windows, так и под Android или iOS. Я тестировал только на Android.
- Далее надо создать классы таблиц БД, к которым будем обращаться из Unity. Ниже приведен пример класса для таблицы Items. Вам нужно будет создать свои классы для каждой таблицы в согласии с вашими наименованиями полей и типами хранимых данных. Названия классов повторяют имена таблиц в БД, а наименования переменных-членов класса соответственно идентичны полям в таблице. Называем как нибудь файл скрипта, например Tables.cs, и сохраняем также в папку [путь_к_вашему_проекту_Unity]\Assets\Scripts\ :
using SQLite4Unity3d; public class Items { [PrimaryKey, AutoIncrement] public int Id { get; set; } public string Item { get; set; } public string Type { get; set; } public string Subtype { get; set; } public int Weight { get; set; } }
- Для иллюстрации работы с БД привожу пример:
using UnityEngine; using System.Collections.Generic; public class PlayerScript : MonoBehaviour { public DataService ds; // Use this for initialization void Start () { // подключение к БД ds = new DataService("db.bytes"); // создание таблицы (таблица будет создана на основании данных класса Items, код которого приведен выше) ds._connection.CreateTable(); // аналог запроса INSERT INTO `Items` (`Item`, `Type`, `Sybtype`, `Weight`) VALUES ("Flyer", "aircraft", "glider", 500) ds._connection.Insert (new Items{ Item = "Flyer", Type = "aircraft", Sybtype = "glider", Weight = 500 }); // аналог запроса SELECT * FROM `Items` WHERE `Id`=1 Items item1 = ds._connection.Table ().Where (x => x.Id == 1).FirstOrDefault (); // для примера выводим в консоль отладки значение поля Weight у записи с Id = 1 Debug.Log(item1.Weight); // удаление таблицы Items ds._connection.DropTable(); } // Update is called once per frame void Update () { } }
Как видно из комментариев, сначала создается таблица Items на основании одноименного класса. Затем в нее вставляется запись. Следующим запросом из записи извлекаются данные и выводятся в консоль отладки. В конце таблица уничтожается.
Чтобы протестировать этот код в вашем проекте, прикрепите его к какому нибудь объекту на сцене.
Кстати, вместо команды Insert можно использовать Update для обновления данных или InsertOrReplace для вставки или замены. Только в этом случае нужно добавить в объект еще и поле Id, для того чтобы можно было идентифицировать запись:
ds._connection.InsertOrReplace (new Items{
Id = 1,
Item = "Flyer",
Type = "aircraft",
Sybtype = "glider",
Weight = 1000
});
Понятно что в примере приведены простейшие запросы, но этого уже достаточно чтобы начать использовать SQLite в Unity.
В заключение ещё один момент. При компиляции проекта для Andriod, Unity выдал ошибку — ему не понравились «лишние» кроссплатформенные библиотеки SQLite в папке Assets\Plugins\. Пришлось на время компиляции убрать оттуда вложенные папки WP8, x64 и x86. После компиляции возвращаем их назад, а то теперь проект не будет запускаться в отладчике Unity. Может есть более удобное решение, но я пока его не нашел (да и не искал).
Добрый день, у меня такой не много глупый вопрос:
У меня есть таблицы items и items_desc_rus и items_desc_eng. Скажите обязательно ли для каждой таблиц items_desc_eng и items_desc_rus создавать отдельный класс или можно выбрать нужную таблицу при запросе? Если да, то как?=))
В приведенном мной примере, да — для каждой новой таблицы нужно создавать новый класс.
А так сделать можно почти все) А как… К сожалению не располагаю достаточным временем для Unity сейчас (им занимался на уровне хобби).
Здравствуйте, если вас не затруднит, помогите решить мою проблему. Я в своем приложении Unity запрашиваю данные из БД SQLite (тыкаю на кнопку и происходит автозаполнение GUI формы). Библиотека sqlite3.dll x64 в папке Plugins. Когда я компилю для Win x86_64 — все работает. Но когда компилирую просто под x86 — кнопка для копирования данных из БД просто не появляется (как будто скрипт отвалился из-за ошибки). Может можно как-то одновременно расположить библиотеки sql и для x32, и x64 и использовать в зависимости от сборки ? (они одноименные и в одной папке обе не положить)
Добрый день, Владимир. Как я писал в своем посте, у меня тоже была проблема с библиотеками. Я решил ее, убирая ненужные перед компиляцией. В вашем случае я бы попробовал убрать x86_64 библиотеку, при компиляции под х86. Конечно, это не очень удобно, но другого способа я пока не знаю.
Спасибо за ответ) Я тоже так делал, но хотелось бы автоматизировать этот процесс, ну да ладно.