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:
    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\ :
    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. Для иллюстрации работы с БД привожу пример:
    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. Может есть более удобное решение, но я пока его не нашел (да и не искал).