Сегодня мы разберёмся как подключить проект на Unity3D к БД MySQL.
Если точнее, то мы разберём как сохранять в БД состояния игровых объектов, а затем загружать их из БД. За основу взят гайд Джонатана Вуда для Unity 3.0.0f5 и MySQL 5.2.28. Он дополнен и адаптирован для 4-й версии Юнити.
Нам понадобятся:
1. Unity3D 4.x.
2. Доступ к БД MySQL.
3. Библиотека MySql.Data.dll.
В папке с проектом необходимо создать каталог Libraries, в который необходимо поместить библиотеку MySQL.Data.dll. Её можно скачать из этого архива, но если у вас уже установлен MySQL, то можете скопировать из c:Program Files (x86)MySQLMySQL Connector Net 6.0.3Assemblies.
Чтобы избежать проблем совместимости версий сборок, а также сэкономить себе несколько часов времени и нервов (спасибо Джоэлю) установите уровень API совместимости Edit --> Project Settings --> Player -->Optimization-->Api Compatibility Level = .NET 2.0.
Предварительная работа:
1. Объекты состояние, которых необходимо сохранять помечаем тэгом «Savable».
2. Создаём таблицу в БД, в которой будет храниться информация об объектах.
Чтобы пользоваться новой библиотекой добавьте в начало скрипта:
using MySql.Data;
using MySql.Data.MySqlClient;
А также не забудьте прописать параметры подключения в переменную:
// MySQL настройки
string constr = "Server=localhost;Database=demo;User ID=demo;Password=demo;Pooling=true;CharSet=utf8;";
Создаём структуру, в которой будет храниться информация об объектах:
// Названия синхронизируемых полей таблицы в БД
string ID, Name, levelname, objectType;
float posx, posy, posz, tranx, trany, tranz;
// описание структуры синхронизируемых данных
public struct data
{
public int UID;
public string ID, Name, levelname, objectType;
public float posx, posy, posz, tranx, trany, tranz;
}
// коллекция в которую будут записываться данные о синхронизируемых объектах
List<data> _GameItems;
При запуске скрипт подключается к БД, а при выключении разрывает соединение:
void Awake()
{
try
{
// установка элемента соединения
con = new MySqlConnection(constr);
// посмотрим, сможем ли мы установить соединение
con.Open();
Debug.Log("Connection State: " + con.State);
}
catch (IOException ex) {Debug.Log(ex.ToString());}
}
void OnApplicationQuit()
{
Debug.Log("killing con");
if (con != null)
{
// Конечно, правильнее использовать:
// if (con.State != ConnectionState.Closed)
// но из-за проблем с версиями сборок приходится использовать костыли
if (con.State.ToString()!="Closed")
con.Close();
con.Dispose();
}
}
Есть две процедуры, отвечающие за работу со объектами на сцене:
/// Этот метод подготавливает данные для записи в БД
void prepData()
{
bodies = GameObject.FindGameObjectsWithTag("Savable");
_GameItems = new List<data>();
data itm;
foreach (GameObject body in bodies)
{
itm = new data();
itm.ID = body.name + "_" + body.GetInstanceID();
itm.Name = body.name;
itm.levelname = Application.loadedLevelName;
itm.objectType = body.name.Replace("(Clone)", "");
itm.posx = body.transform.position.x;
itm.posy = body.transform.position.y;
itm.posz = body.transform.position.z;
itm.tranx = body.transform.rotation.x;
itm.trany = body.transform.rotation.y;
itm.tranz = body.transform.rotation.z;
_GameItems.Add(itm);
}
Debug.Log("Items in collection: " + _GameItems.Count);
}
}
/// Этот метод устанавливает загруженные объекты на сцене
void loadData()
{
// Удаляем все лишние объекты со сцены
bodies = GameObject.FindGameObjectsWithTag("Savable");
DestroyObject(bodies);
// Создаём сохранённые в БД объекты
if (_GameItems != null)
{
if (_GameItems.Count > 0)
{
foreach (data itm in _GameItems)
{
if (itm.objectType="CUBE")
{Instantiate(mCUBE, new Vector3(itm.posx, itm.posy , itm.posz),Quaternion.Euler(itm.tranx, itm.trany, itm.tranz));}
else if (itm.objectType="SPHERE")
{Instantiate(mSPHERE, new Vector3(itm.posx, itm.posy , itm.posz),Quaternion.Euler(itm.tranx, itm.trany, itm.tranz));}
else if (itm.objectType="TRIANGLE")
{Instantiate(mTRIANGLE, new Vector3(itm.posx, itm.posy , itm.posz),Quaternion.Euler(itm.tranx, itm.trany, itm.tranz));}
}
}
}
}
}
А также четыре основных процедуры, отвечающие за работу с БД:
// Вставка новой записи в таблицу
void InsertEntries()
{
prepData();
string query = string.Empty;
// Вылавливаем ошибки
try
{
query = "INSERT INTO demo_table (ID, Name, levelname, objectType, posx, posy, posz, tranx, trany, tranz) VALUES (?ID, ?Name, ?levelname, ?objectType, ?posx, ?posy, ?posz, ?tranx, ?trany, ?tranz)";
if (con.State.ToString()!="Open")
con.Open();
using (con)
{
foreach (data itm in _GameItems)
{
using (cmd = new MySqlCommand(query, con))
{
MySqlParameter oParam = cmd.Parameters.Add("?ID", MySqlDbType.VarChar);
oParam.Value = itm.ID;
MySqlParameter oParam1 = cmd.Parameters.Add("?Name", MySqlDbType.VarChar);
oParam1.Value = itm.Name;
MySqlParameter oParam2 = cmd.Parameters.Add("?levelname", MySqlDbType.VarChar);
oParam2.Value = itm.levelname;
MySqlParameter oParam3 = cmd.Parameters.Add("?objectType", MySqlDbType.VarChar);
oParam3.Value = itm.objectType;
MySqlParameter oParam4 = cmd.Parameters.Add("?posx", MySqlDbType.Float);
oParam4.Value = itm.posx;
MySqlParameter oParam5 = cmd.Parameters.Add("?posy", MySqlDbType.Float);
oParam5.Value = itm.posy;
MySqlParameter oParam6 = cmd.Parameters.Add("?posz", MySqlDbType.Float);
oParam6.Value = itm.posz;
MySqlParameter oParam7 = cmd.Parameters.Add("?tranx", MySqlDbType.Float);
oParam7.Value = itm.tranx;
MySqlParameter oParam8 = cmd.Parameters.Add("?trany", MySqlDbType.Float);
oParam8.Value = itm.trany;
MySqlParameter oParam9 = cmd.Parameters.Add("?tranz", MySqlDbType.Float);
oParam9.Value = itm.tranz;
cmd.ExecuteNonQuery();
}
}
}
}
catch (IOException ex)
{
Debug.Log(ex.ToString());
}
finally {}
}
// Обновление существующих записей в таблице
void UpdateEntries()
{
prepData();
string query = string.Empty;
// Вылавливаем ошибки
try
{
query = "UPDATE demo_table SET ID=?ID, Name=?Name, levelname=?levelname, objectType=?objectType, posx=?posx, posy=?posy, posz=?posz, tranx=?tranx, trany=?trany, tranz=?tranz WHERE iddemo_table=?UID";
if (con.State.ToString()!="Open")
con.Open();
using (con)
{
foreach (data itm in _GameItems)
{
using (cmd = new MySqlCommand(query, con))
{
MySqlParameter oParam = cmd.Parameters.Add("?ID", MySqlDbType.VarChar);
oParam.Value = itm.ID;
MySqlParameter oParam1 = cmd.Parameters.Add("?Name", MySqlDbType.VarChar);
oParam1.Value = itm.Name;
MySqlParameter oParam2 = cmd.Parameters.Add("?levelname", MySqlDbType.VarChar);
oParam2.Value = itm.levelname;
MySqlParameter oParam3 = cmd.Parameters.Add("?objectType", MySqlDbType.VarChar);
oParam3.Value = itm.objectType;
MySqlParameter oParam4 = cmd.Parameters.Add("?posx", MySqlDbType.Float);
oParam4.Value = itm.posx;
MySqlParameter oParam5 = cmd.Parameters.Add("?posy", MySqlDbType.Float);
oParam5.Value = itm.posy;
MySqlParameter oParam6 = cmd.Parameters.Add("?posz", MySqlDbType.Float);
oParam6.Value = itm.posz;
MySqlParameter oParam7 = cmd.Parameters.Add("?tranx", MySqlDbType.Float);
oParam7.Value = itm.tranx;
MySqlParameter oParam8 = cmd.Parameters.Add("?trany", MySqlDbType.Float);
oParam8.Value = itm.trany;
MySqlParameter oParam9 = cmd.Parameters.Add("?tranz", MySqlDbType.Float);
oParam9.Value = itm.tranz;
cmd.ExecuteNonQuery();
}
}
}
}
catch (IOException ex) {Debug.Log(ex.ToString());}
finally {}
}
// Удаляем запись из таблицы
void DeleteEntries()
{
string query = string.Empty;
// Вылавливаем ошибки
try
{
// лучше всего если вы знаете ID записи, которую необходимо удалить
//-----------------------------------------------------------------------
// query = "DELETE FROM demo_table WHERE iddemo_table=?UID";
// MySqlParameter oParam = cmd.Parameters.Add("?UID", MySqlDbType.Int32);
// oParam.Value = 0;
//-----------------------------------------------------------------------
query = "DELETE FROM demo_table WHERE iddemo_table";
if (con.State.ToString()!="Open")
con.Open();
using (con)
{
using (cmd = new MySqlCommand(query, con))
{
cmd.ExecuteNonQuery();
}
}
}
catch (IOException ex) {Debug.Log(ex.ToString());}
finally {}
}
// Чтение всех записей из таблицы
void ReadEntries()
{
string query = string.Empty;
if (_GameItems == null)
_GameItems = new List<data>();
if (_GameItems.Count > 0)
_GameItems.Clear();
// Отлавливаем ошибки
try
{
query = "SELECT * FROM view_demo";
if (con.State.ToString()!="Open")
con.Open();
using (con)
{
using (cmd = new MySqlCommand(query, con))
{
rdr = cmd.ExecuteReader();
if(rdr.HasRows)
while (rdr.Read())
{
data itm = new data();
itm.UID = int.Parse(rdr["iddemo_table"].ToString());
itm.ID = rdr["ID"].ToString();
itm.levelname = rdr["levelname"].ToString();
itm.Name = rdr["Name"].ToString();
itm.objectType = rdr["objectType"].ToString();
itm.posx = float.Parse(rdr["posx"].ToString());
itm.posy = float.Parse(rdr["posy"].ToString());
itm.posz = float.Parse(rdr["posz"].ToString());
itm.tranx = float.Parse(rdr["tranx"].ToString());
itm.trany = float.Parse(rdr["trany"].ToString());
itm.tranz = float.Parse(rdr["tranz"].ToString());
_GameItems.Add(itm);
}
rdr.Dispose();
}
}
}
catch (IOException ex) {Debug.Log(ex.ToString());}
finally {}
}
Остаётся только сделать пару кнопок для загрузки и сохранения сцены:
// Кнопки загрузки и сохранения
void OnGUI()
{
if (GUI.Button(new Rect(10, 70, 50, 30), "Save") && !saving)
{
saving = true;
// Для начала очистим таблицу
DeleteEntries();
// теперь сохраним информацию о сцене
InsertEntries();
// можно также использовать обновление если известен ID уже сохранённого элемента
saving = false;
}
if (GUI.Button(new Rect(10, 110, 50, 30), "Load") && !loading)
{
loading = true;
// считаем информацию из БД
ReadEntries();
// создадим загруженные объекты
loadData();
// теперь отобразим информацию из лога
LogGameItems();
loading = false;
}
}
Вот в общем-то и всё. В комментах приветствуются примечания и дополнения.
using UnityEngine;
using MySql.Data;
using MySql.Data.MySqlClient;
using System.IO;
using System.Collections;
using System.Collections.Generic;
public class MySQLCS : MonoBehaviour
{
public GameObject mCUBE;
public GameObject mSPHERE;
public GameObject mTRIANGLE;
bool saving = false;
bool loading = false;
// MySQL настройки
string constr = "Server=localhost;Database=demo;User ID=demo;Password=demo;Pooling=true;CharSet=utf8;";
// соединение
MySqlConnection con = null;
// команда к БД
MySqlCommand cmd = null;
// чтение
MySqlDataReader rdr = null;
// ошибки
MySqlError er = null;
// массив синхронизируемых игровых объектов
GameObject[] bodies;
// Названия синхронизируемых полей таблицы в БД
string ID, Name, levelname, objectType;
float posx, posy, posz, tranx, trany, tranz;
// описание структуры синхронизируемых данных
public struct data
{
public int UID;
public string ID, Name, levelname, objectType;
public float posx, posy, posz, tranx, trany, tranz;
}
// коллекция в которую будут записываться данные о синхронизируемых объектах
List<data> _GameItems;
void Awake()
{
try
{
// установка элемента соединения
con = new MySqlConnection(constr);
// посмотрим, сможем ли мы установить соединение
con.Open();
Debug.Log("Connection State: " + con.State);
}
catch (IOException ex) {Debug.Log(ex.ToString());}
}
void OnApplicationQuit()
{
Debug.Log("killing con");
if (con != null)
{
// Конечно, правильнее использовать:
// if (con.State != ConnectionState.Closed)
// но из-за проблем с версиями сборок приходится использовать костыли
if (con.State.ToString()!="Closed")
con.Close();
con.Dispose();
}
}
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
}
// Кнопки загрузки и сохранения
void OnGUI()
{
if (GUI.Button(new Rect(10, 70, 50, 30), "Save") && !saving)
{
saving = true;
// Для начала очистим таблицу
DeleteEntries();
// теперь сохраним информацию о сцене
InsertEntries();
// можно также использовать обновление если известен ID уже сохранённого элемента
saving = false;
}
if (GUI.Button(new Rect(10, 110, 50, 30), "Load") && !loading)
{
loading = true;
// считаем информацию из БД
ReadEntries();
// создадим загруженные объекты
loadData();
// теперь отобразим информацию из лога
LogGameItems();
loading = false;
}
}
// Вставка новой записи в таблицу
void InsertEntries()
{
prepData();
string query = string.Empty;
// Вылавливаем ошибки
try
{
query = "INSERT INTO demo_table (ID, Name, levelname, objectType, posx, posy, posz, tranx, trany, tranz) VALUES (?ID, ?Name, ?levelname, ?objectType, ?posx, ?posy, ?posz, ?tranx, ?trany, ?tranz)";
if (con.State.ToString()!="Open")
con.Open();
using (con)
{
foreach (data itm in _GameItems)
{
using (cmd = new MySqlCommand(query, con))
{
MySqlParameter oParam = cmd.Parameters.Add("?ID", MySqlDbType.VarChar);
oParam.Value = itm.ID;
MySqlParameter oParam1 = cmd.Parameters.Add("?Name", MySqlDbType.VarChar);
oParam1.Value = itm.Name;
MySqlParameter oParam2 = cmd.Parameters.Add("?levelname", MySqlDbType.VarChar);
oParam2.Value = itm.levelname;
MySqlParameter oParam3 = cmd.Parameters.Add("?objectType", MySqlDbType.VarChar);
oParam3.Value = itm.objectType;
MySqlParameter oParam4 = cmd.Parameters.Add("?posx", MySqlDbType.Float);
oParam4.Value = itm.posx;
MySqlParameter oParam5 = cmd.Parameters.Add("?posy", MySqlDbType.Float);
oParam5.Value = itm.posy;
MySqlParameter oParam6 = cmd.Parameters.Add("?posz", MySqlDbType.Float);
oParam6.Value = itm.posz;
MySqlParameter oParam7 = cmd.Parameters.Add("?tranx", MySqlDbType.Float);
oParam7.Value = itm.tranx;
MySqlParameter oParam8 = cmd.Parameters.Add("?trany", MySqlDbType.Float);
oParam8.Value = itm.trany;
MySqlParameter oParam9 = cmd.Parameters.Add("?tranz", MySqlDbType.Float);
oParam9.Value = itm.tranz;
cmd.ExecuteNonQuery();
}
}
}
}
catch (IOException ex)
{
Debug.Log(ex.ToString());
}
finally {}
}
// Обновление существующих записей в таблице
void UpdateEntries()
{
prepData();
string query = string.Empty;
// Вылавливаем ошибки
try
{
query = "UPDATE demo_table SET ID=?ID, Name=?Name, levelname=?levelname, objectType=?objectType, posx=?posx, posy=?posy, posz=?posz, tranx=?tranx, trany=?trany, tranz=?tranz WHERE iddemo_table=?UID";
if (con.State.ToString()!="Open")
con.Open();
using (con)
{
foreach (data itm in _GameItems)
{
using (cmd = new MySqlCommand(query, con))
{
MySqlParameter oParam = cmd.Parameters.Add("?ID", MySqlDbType.VarChar);
oParam.Value = itm.ID;
MySqlParameter oParam1 = cmd.Parameters.Add("?Name", MySqlDbType.VarChar);
oParam1.Value = itm.Name;
MySqlParameter oParam2 = cmd.Parameters.Add("?levelname", MySqlDbType.VarChar);
oParam2.Value = itm.levelname;
MySqlParameter oParam3 = cmd.Parameters.Add("?objectType", MySqlDbType.VarChar);
oParam3.Value = itm.objectType;
MySqlParameter oParam4 = cmd.Parameters.Add("?posx", MySqlDbType.Float);
oParam4.Value = itm.posx;
MySqlParameter oParam5 = cmd.Parameters.Add("?posy", MySqlDbType.Float);
oParam5.Value = itm.posy;
MySqlParameter oParam6 = cmd.Parameters.Add("?posz", MySqlDbType.Float);
oParam6.Value = itm.posz;
MySqlParameter oParam7 = cmd.Parameters.Add("?tranx", MySqlDbType.Float);
oParam7.Value = itm.tranx;
MySqlParameter oParam8 = cmd.Parameters.Add("?trany", MySqlDbType.Float);
oParam8.Value = itm.trany;
MySqlParameter oParam9 = cmd.Parameters.Add("?tranz", MySqlDbType.Float);
oParam9.Value = itm.tranz;
cmd.ExecuteNonQuery();
}
}
}
}
catch (IOException ex) {Debug.Log(ex.ToString());}
finally {}
}
// Удаляем запись из таблицы
void DeleteEntries()
{
string query = string.Empty;
// Вылавливаем ошибки
try
{
// лучше всего если вы знаете ID записи, которую необходимо удалить
//-----------------------------------------------------------------------
// query = "DELETE FROM demo_table WHERE iddemo_table=?UID";
// MySqlParameter oParam = cmd.Parameters.Add("?UID", MySqlDbType.Int32);
// oParam.Value = 0;
//-----------------------------------------------------------------------
query = "DELETE FROM demo_table WHERE iddemo_table";
if (con.State.ToString()!="Open")
con.Open();
using (con)
{
using (cmd = new MySqlCommand(query, con))
{
cmd.ExecuteNonQuery();
}
}
}
catch (IOException ex) {Debug.Log(ex.ToString());}
finally {}
}
// Чтение всех записей из таблицы
void ReadEntries()
{
string query = string.Empty;
if (_GameItems == null)
_GameItems = new List<data>();
if (_GameItems.Count > 0)
_GameItems.Clear();
// Отлавливаем ошибки
try
{
query = "SELECT * FROM view_demo";
if (con.State.ToString()!="Open")
con.Open();
using (con)
{
using (cmd = new MySqlCommand(query, con))
{
rdr = cmd.ExecuteReader();
if(rdr.HasRows)
while (rdr.Read())
{
data itm = new data();
itm.UID = int.Parse(rdr["iddemo_table"].ToString());
itm.ID = rdr["ID"].ToString();
itm.levelname = rdr["levelname"].ToString();
itm.Name = rdr["Name"].ToString();
itm.objectType = rdr["objectType"].ToString();
itm.posx = float.Parse(rdr["posx"].ToString());
itm.posy = float.Parse(rdr["posy"].ToString());
itm.posz = float.Parse(rdr["posz"].ToString());
itm.tranx = float.Parse(rdr["tranx"].ToString());
itm.trany = float.Parse(rdr["trany"].ToString());
itm.tranz = float.Parse(rdr["tranz"].ToString());
_GameItems.Add(itm);
}
rdr.Dispose();
}
}
}
catch (IOException ex) {Debug.Log(ex.ToString());}
finally {}
}
/// Запишем в лог всё что было считано из БД
void LogGameItems()
{
if (_GameItems != null)
{
if (_GameItems.Count > 0)
{
foreach (data itm in _GameItems)
{
Debug.Log("UID: " + itm.UID);
Debug.Log("ID: " + itm.ID);
Debug.Log("levelname: " + itm.levelname);
Debug.Log("Name: " + itm.Name);
Debug.Log("objectType: " + itm.objectType);
Debug.Log("posx: " + itm.posx);
Debug.Log("posy: " + itm.posy);
Debug.Log("posz: " + itm.posz);
Debug.Log("tranx: " + itm.tranx);
Debug.Log("trany: " + itm.trany);
Debug.Log("tranz: " + itm.tranz);
}
}
}
}
/// Этот метод подготавливает данные для записи в БД
void prepData()
{
bodies = GameObject.FindGameObjectsWithTag("Savable");
_GameItems = new List<data>();
data itm;
foreach (GameObject body in bodies)
{
itm = new data();
itm.ID = body.name + "_" + body.GetInstanceID();
itm.Name = body.name;
itm.levelname = Application.loadedLevelName;
itm.objectType = body.name.Replace("(Clone)", "");
itm.posx = body.transform.position.x;
itm.posy = body.transform.position.y;
itm.posz = body.transform.position.z;
itm.tranx = body.transform.rotation.x;
itm.trany = body.transform.rotation.y;
itm.tranz = body.transform.rotation.z;
_GameItems.Add(itm);
}
Debug.Log("Items in collection: " + _GameItems.Count);
}
}
/// Этот метод устанавливает загруженные объекты на сцене
void loadData()
{
// Удаляем все лишние объекты со сцены
bodies = GameObject.FindGameObjectsWithTag("Savable");
DestroyObject(bodies);
// Создаём сохранённые в БД объекты
if (_GameItems != null)
{
if (_GameItems.Count > 0)
{
foreach (data itm in _GameItems)
{
if (itm.objectType="CUBE") {Instantiate(mCUBE, new Vector3(itm.posx, itm.posy , itm.posz),Quaternion.Euler(itm.tranx, itm.trany, itm.tranz));}
else if (itm.objectType="SPHERE") {Instantiate(mSPHERE, new Vector3(itm.posx, itm.posy , itm.posz),Quaternion.Euler(itm.tranx, itm.trany, itm.tranz));}
else if (itm.objectType="TRIANGLE") {Instantiate(mTRIANGLE, new Vector3(itm.posx, itm.posy , itm.posz),Quaternion.Euler(itm.tranx, itm.trany, itm.tranz));}
}
}
}
}
}
Автор: malan
не фига не работает