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

1062: Duplicate entry ’2147483647′ for key ‘PRIMARY’ или Проблема 2038 года

Наверное помните раскрученную проблему 2000? А все из-за того что кто-то заранее не подумал о том что будет через каких то 30 лет и использовал в программном обеспечении 2-ух значное обозначение года. И надо сказать, что проблема 2000 не единственная в своем роде. Наверное еще рановато об этом говорить, но назревает еще и проблема 2038 года. Поясню что имеется в виду. Попробуйте запустить следующий PHP-код:

1
<?php echo date("d.m.Y H:i", 2147483647); ?>

На выходе получим: 19.01.2038 06:14. А теперь увеличьте число на 1. Получилось 13.12.1901 23:15 что не соответствует истине. А все потому что метка времени UNIX при этом выходит за рамки диапазона типа данных Integer.

Хотя проблема 2038 года — это конечно больше шутка. Но однажды при записи данных в MySQL базу данных я получил ошибку:

1062: Duplicate entry ’2147483647′ for key ‘PRIMARY’

Дело в том что для первичного ключа использовалось поле с типом данных INT. И так получилось, что AUTO INCREMENT вышел за пределы числа 2147483647, максимального для INT. Решение простое в данном случае — пришлось использовать тип данных BIGINT. Так что думайте сами, решайте сами …

Не запускается The Crew

В гости приехал племянник, которому подарили Uplay-ключ игры The Crew. Естественно захотели запустить его (её?) на нашем «слегка игровом» HTPC. К тому моменту на компьютере уже было 4Gb RAM (2 шт по 2Gb, то есть в двухканальном режиме). Минимальные требования игры, помимо памяти, заявлены такие: процессор Intel Core2 Quad Q9300 @ 2,5 ГГц или AMD Athlon II X4 620 @ 2,6 ГГц; видеокарта NVIDIA GeForce GTX260 или AMD Radeon HD4870. Это несколько мощнее чем начинка имеющегося HTPC (g1620 + GT630), но не намного. И как оказалось позднее, комфортно поиграть на средних настройках (и на среднем разрешении) вполне можно. Но вначале игра вообще не запускалась. На тот момент была установлена Windows 7 32bit, которая естественно видела только 3,5Gb ОЗУ из 4-ёх, и как оказалось вообще не поддерживала Crew.

Переустановили Win 7 64bit. Но игра по прежнему не подавала признаков жизни. Никаких ошибок не отображалось, просто вообще нет реакции на запуск. Решили немного обновить компоненты системы, в частности MS Framework — ничего не изменилось.

Топтались на месте довольно долго, потом решили попробовать запустить игру от Администратора. Также назначили запуск от Админа и для клиентов Uplay и Steam (он в данном случае тоже был задействован — хотя игра от Uplay, она приписана к аккаунту Steam). После этого наметился определенный прогресс — на экране появилась ошибка! В ней система нам поведала что для запуска не хватает xinput1_3.dll.

Скачали указанную библиотеку и положили её в папку игры: c:\Program Files (x86)\Steam\steamapps\common\The Crew\. А также в папку c:\windows\SysWOW64\. Но не тут то было — теперь выпала ошибка с кодом 0xc000007b. В связи с этим нашли информацию, что надо обновить DirectX. Обновили с помощью утилиты dxwebsetup.exe (её можно скачать на официальном сайте Microsoft). Но дело было не в этом — ошибка появилась вновь.

В конце концов, когда уже почти отчаялись запустить Crew, помог неожиданный ход. После того как попробовали несколько версий xinput1_3.dll, найденных в сети, и ни одна не подошла, обратили внимание что в папке Uplay тоже есть такая библиотека. Скопировали именно её в вышеупомянутые папки c:\Program Files (x86)\Steam\steamapps\common\The Crew\ и  c:\windows\SysWOW64\. Запуск игры произвели через файл TheCrewLauncher.exe из папки The Crew. После этого всё наконец заработало!

В общем Win 64bit нужен без вопросов. Возможно один из промежуточных упомянутых шагов тоже сыграл роль, проверять повторно не стали. Но решающим было именно копирование xinput1_3.dll из папки Uplay в системную папку c:\windows\SysWOW64\ и папку игры. У некоторых, похоже, бывают проблемы запуска The Crew немного иного характера, но может данный опыт тоже пригодится.

Отображение количества просмотров поста в WordPress по данным Yandex.metrika

Вместо того чтобы создавать свою систему статистики для блога, почему бы не использовать уже готовую — например Yandex.metrika? Тем более что Yandex предоставляет свой API для доступа к статистике. Ниже приведен способ извлечения с Yandex.metrika и отображения количества просмотров для конкретного поста.

Первый шаг — регистрация приложения для доступа к статистике. Для этого авторизуемся в своем аккаунте метрики. Затем переходим по ссылке:
https://oauth.yandex.ru/client/new
И регистрируем приложение как указано на скриншоте.

yandex metrika

Второй шаг — узнаем ID своего приложения. Для этого идем сюда:
https://oauth.yandex.ru/client/my
Кликаем на названии своего приложения и копируем куда нибудь его ID.

Третий шаг — нужно получить токен для доступа к системе. Для этого выполняем следующий запрос, подставив ID приложения полученный на предыдущем шаге:

https://oauth.yandex.ru/authorize?response_type=token&client_id=[подставляем ID приложения]

Копируем токен, который понадобится скрипту для доступа к статистике. Кстати, для проверки правильности предыдущих шагов можно выполнить запрос вида:

http://api-metrika.yandex.ru/stat/content/popular?id=[номер счетчика метрики]&date1=[дата начала ГГГГММДД]&date2=[дата конца ГГГГММДД]&oauth_token=[токен]

Если вы увидите xml-файл с данными статистики, то все нормально.

Четвертый шаг — нужно добавить скрипт в WordPress. Создаем в корне сайта файл yml.php:

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
<?php
// НАСТРОЙКИ
$startDate = ""; // дата с которой считать статистику в формате ГГГГММДД
$yandexMetrikaCounterID = ""; // № счетчика Yandex.metrika
$yandexMetrikaToken = ""; // токен
 
// инициализация
$ymDateLoad = 0;
 
// если xml-фаил метрики есть
if (file_exists($_SERVER['DOCUMENT_ROOT'].'/upload/ym.xml')) {
	$ymData = simplexml_load_file($_SERVER['DOCUMENT_ROOT'].'/upload/ym.xml');
	$ymDateLoad = $ymData->date2;
}
 
// если фаила метрики нет или он устарел
if ($ymDateLoad != date('Ymd')) {
	// создание нового cURL ресурса
	$ch = curl_init();
 
	// установка URL и других необходимых параметров
	curl_setopt($ch, CURLOPT_URL, "http://api-metrika.yandex.ru/stat/content/popular?id=".$yandexMetrikaCounterID."&date1=".$startDate."&date2=".date("Ymd")."&oauth_token=".$yandexMetrikaToken);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_HEADER, 0);
 
	// загрузка страницы
	$ymXml = curl_exec($ch);
 
	// завершение сеанса и освобождение ресурсов
	curl_close($ch);
 
	// обработка
	$ymXml = str_replace(array('<![CDATA[', ']]>', str_replace('www.', '', 'http://'.$_SERVER['HTTP_HOST'])), array('', '', ''), $ymXml);
 
	// запись в фаил
	$handle = fopen($_SERVER['DOCUMENT_ROOT']."/upload/ym.xml", "w");
	fwrite($handle, $ymXml);
	fclose($handle);
 
	// объект SimpleXML
	$ymData = simplexml_load_string($ymXml);
	$ymDateLoad = $ymData->date2;
}
 
if ($ymDateLoad) {
	// uri
	$uri = $_SERVER['REQUEST_URI'];
 
	// вывод
	foreach ($ymData->data->row as $row) {
		if ($uri!=$row->url) continue;
		echo $row->page_views." просмотров";
	}
}
?>

В самом начале кода в разделе настроек присваиваем значения 3-ём переменным. $startDate — дата начала статистики в формате ГГГГММДД (например 20130101). Логично поставить дату установки счетчика метрики на блоге. $yandexMetrikaCounterID — подставляем номер счетчика Yandex.metrika. Найти его можно здесь:

yandex metrika

$yandexMetrikaToken — сюда подставляем токен полученный на 3-ем шаге.

Пятый шаг — создаем папку для файлов кеша. Работать и без этого будет, конечно, но тогда каждый раз при отображении поста будет тратится лишнее время на подгрузку статистики с метрики. Итак, создаем в корне сайта папку upload и присваиваем ей права 777. Например в FileZilla это делается так:

yandex metrika

Шестой шаг — добавляем отображение количества просмотров в шаблон поста. В панели управления WordPress идем в Внешний вид -> Редактор и выбираем шаблон content-single.php. Вставляем следующий код:

<div><?php require_once($_SERVER['DOCUMENT_ROOT']."/ymc.php"); ?></div>

например, после заголовка поста, то есть после следующего кода:

<h1><?php the_title(); ?></h1>

Так же можно настроить стили, добавив в файл style.css, например, следующее:

.ymc {color:#218f48; font-weight:bold;}

В результате работы скрипта получим вот это:

yandex metrika


Так же интересно…
Потолок, а так же его правильное освещение, к примеру, с использованием люстры для спальни — может полностью изменить интерьер любого помещения, будь то офис или жилая комната.


После установки Windows 7 ошибка: The computer restarted unexpectedly or encountered an unexpected error. Windows installation cannot proceed

После неудачной установки эксперементального Linux совместно с Windows 7, а также неудачной разметки разделов, перестали загружаться вообще все системы. Попытка восстановления Windows не дала результат. После этого начал чистую установку Windows 7. Из установщика удалил все старые разделы, разметил по новому и начал установку. Однако на завершающем этапе получил ошибку: «The computer restarted unexpectedly or encountered an unexpected error. Windows installation cannot proceed». Ошибка циклическая — то есть после перезагрузки, которую советовал сделать установщик, она появлялась вновь.

Решение, кстати, нашлось на официальном форуме Microsoft. Опишу вкратце что нужно сделать:

  1. Когда появилась указанная выше ошибка, нажимаем Shift+F10 — открывается командная строка — уже легче!
  2. Пишем команду regedit — запускается редактор реестра.
  3. Открываем ветку HKEY LOCAL MACHINE/SYSTEM/SETUP/STATUS/ChildCompletion. Справа выбираем параметр setup.exe и меняем его значение с 1 на 3. Жмем ОК.
  4. Реестр закрываем и теперь можно нажимать ОК в окне ошибки. Начинается перезагрузка.

После указанных действий, загрузка Windows 7 прошла успешно. Причина возникновения ошибки скорее всего в том, что загрузчику Windows не понравилось что-то в разметке диска. Однако, как видно, это не повод сразу менять жесткий диск — всего-то надо было указать установщику что его работа закончена, выставив соответствующий флаг в реестре.