SQLite в Unity

Хранение данных в базе данных, извините за тавтологию — это очень удобно. Сегодня мы рассмотрим как организовать подключение к встраиваемой БД SQLite в Unity. Это, на мой взгляд, гораздо лучше чем использовать PlayerPrefs. В примере используется Unity 5.3.1f1.

  1. Создаем БД любым доступным инструментом. Я использовал менеджер SQLite в виде плагина к Firefox. Файл БД надо сохранить в папке [путь_к_вашему_проекту_Unity]\Assets\StreamingAssets\. В примере используется имя файла db.bytes.
  2. Нам понадобятся библиотеки для доступа к SQLite. Скачать их можно отсюда. Скачиваем и распаковываем куда нибудь архив SQLite4Unity3d. Копируем папку Assets\Plugins\ из распакованного архива в [путь_к_вашему_проекту_Unity]\Assets\Plugins\.
  3. Теперь создаем в папке [путь_к_вашему_проекту_Unity]\Assets\Scripts\ файл DataService.cs:
    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
    
    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.

  4. Далее надо создать классы таблиц БД, к которым будем обращаться из Unity. Ниже приведен пример класса для таблицы Items. Вам нужно будет создать свои классы для каждой таблицы в согласии с вашими наименованиями полей и типами хранимых данных. Названия классов повторяют имена таблиц в БД, а наименования переменных-членов класса соответственно идентичны полям в таблице. Называем как нибудь файл скрипта, например Tables.cs, и сохраняем также в папку [путь_к_вашему_проекту_Unity]\Assets\Scripts\ :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    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; }
    }
  5. Для иллюстрации работы с БД привожу пример:
    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
    
    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, для того чтобы можно было идентифицировать запись:

1
2
3
4
5
6
7
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. Может есть более удобное решение, но я пока его не нашел (да и не искал).