Sqlite是一個輕量級數據庫,在Unity中可以很方便的使用它。比如做AssetBundle資源更新時,可以把AssetBundle的描述信息存放在數據庫中;或者將一些數值表生成數據庫文件,作為游戲的功能配置文件等等。。。
導入步驟
先到Sqlite官網下載對應你系統的Sqlite庫(比如windows64系統,對應 PRecompiled Binaries for Windows sqlite-dll-win64-x64)。
在Unity中Assets目錄下新建Plugins/x86_64目錄,解壓將sqlite3.def和sqlite3.dll放到該目錄下。
由于在代碼中需要使用System.Data和Mono.Data.Sqlite庫,這兩個庫在Unity3d的安裝目錄中有。 在unity安裝目錄/Editor/Data/Mono/lib/mono/2.0下,拷貝System.Data.dll 和 Mono.Data.Sqlite.dll放到Plugins下。
在Player Setting里的 OtherSettings里有個Optimization,下邊的API Compatbility Level 選擇.NET 2.0。
(注意,這里我們只下載了windows上的sqltie庫,如果需要在移動端使用則應該去下載對應平臺的sqltie庫。)
此時,工程的目錄結構應該是這樣的
接下來,封裝一個Sqlite類,用于對數據庫的一些基本操作,增、刪、改、查等。
Sqlite.cs
using UnityEngine;using System.Collections;using System.Collections.Generic;using Mono.Data.Sqlite;using System.Data;using System;using System.Reflection;using Object = UnityEngine.Object;namespace Hi{ public class Sqlite { //Sqlite連接前綴 private const string DB_CONNECTION_PREFIX = "URI=file:"; //連接器 private IDbConnection m_dbConn; //查詢命令 private IDbCommand m_command; //事物 private IDbTransaction m_dbTrans; public Sqlite(string path) { OpenDataBase(path); } private void OpenDataBase(string path) { m_dbConn = new SqliteConnection(DB_CONNECTION_PREFIX + path); m_dbConn.Open(); m_command = m_dbConn.CreateCommand(); } //查詢函數 //IConfig是自定義的配置類接口,最好使數據庫中的配置類都繼承同一個接口,方便以后擴展 public IEnumerable ExcuteSelectQuery<T>(string sqlQuery) where T : IConfig { return ExcuteSelectQuery(sqlQuery, typeof(T)); } //查詢函數,這里使用反射 反序列數據,簡化對對象的賦值操作 public IEnumerable ExcuteSelectQuery(string sqlQuery, Type type) { //使用事物 BeginTrans(); //查詢語句 m_command.CommandText = sqlQuery; //查詢結果 IDataReader reader = m_command.ExecuteReader(); //反序列化操作 所定義的變量 PropertyInfo[] newpropertys = null; PropertyInfo[] oldpropertys = null; bool init = false; while (reader.Read()) { //創建類對象 IConfig config = Activator.CreateInstance(type) as IConfig; for (int i = 0; i < reader.FieldCount; i++) { if (!init) { //newpropertys將類的屬性 順序的對應到數據庫中的列名,方便后續的賦值操作 if (newpropertys == null) { newpropertys = new PropertyInfo[reader.FieldCount]; } //獲取類的所有屬性 if (oldpropertys == null) { oldpropertys = type.GetProperties(BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance); } //當前數據的列名 string filedName = reader.GetName(i); bool find = false; PropertyInfo pro = null; //查找與列名相同的自定義特性名稱,相同則把查詢到的數據值賦值到對象中 for (int j = 0; j < oldpropertys.Length; j++) { pro = oldpropertys[j]; //獲取自定義特性 object[] objs = pro.GetCustomAttributes(typeof(ConfigFieldAttribute), false); if (objs.Length == 0) { continue; } //特性的列名是否與數據庫中列名相同 ConfigFieldAttribute cfgfield = objs[0] as ConfigFieldAttribute; if (cfgfield.filedName == filedName) { find = true; break; } } newpropertys[i] = find ? pro : null; } //已經排好序的類屬性數組 PropertyInfo info = newpropertys[i]; if (info == null) { continue; } //對象的屬性賦值 info.SetValue(config, reader.GetValue(i), null); } if (!init) { init = true; } yield return config; } Commit(); reader.Close(); } //使用事物 public void BeginTrans() { m_dbTrans = m_dbConn.BeginTransaction(); m_command.Transaction = m_dbTrans; } //事物回滾 public void Rollback() { m_dbTrans.Rollback(); } //事物生效 public void Commit() { m_dbTrans.Commit(); } //執行其他sql語句 public void ExcuteQuery(string sqlQuery) { m_command.CommandText = sqlQuery; m_command.ExecuteNonQuery(); } //關閉連接 public void Close() { if (m_dbTrans != null) { m_dbTrans.Dispose(); m_dbTrans = null; } if (m_command != null) { m_command.Dispose(); m_command = null; } m_dbConn.Close(); m_dbConn = null; } }}中間那段查詢函數寫得有點麻煩,它的功能是把查詢到的一行數據賦值給一個新對象,后面會詳細說明
IConfig.cs
namespace Hi{ //這里只是想把所有的數據庫配置類抽出一個接口,方便以后擴展 public interface IConfig { }}ConfigFieldAttribute.cs
using UnityEngine;using System.Collections;using System;namespace Hi{ //自定義配置類特性,用于跟數據庫中單條數據匹配 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class ConfigFieldAttribute : Attribute { public string filedName { get; private set; } public ConfigFieldAttribute(string name) { filedName = name; } }}TestConfig.cs
using UnityEngine;using System.Collections;using Hi;public class TestConfig : IConfig{ //對應數據庫中列名為ID的數據 [ConfigField("ID")] public string m_id { get; set; } [ConfigField("Name")] public string m_name { get; set; } [ConfigField("Len")] public int m_len { get; set; } public override string ToString() { return string.Format("Id : {0}, Name : {1}, Len : {2}", m_id, m_name, m_len); }}Test.cs
using UnityEngine;using System.Collections;using Hi;using System.Text;public class Test : MonoBehaviour{ private Sqlite m_sqlite; private const string TABLE_NAME = "UnityTest"; // Use this for initialization void Start() { //數據庫地址 string dbPath = application.dataPath + "/test.db"; m_sqlite = new Sqlite(dbPath); //創建新表 CreateTable(); //插入數據 TestConfig tc1 = new TestConfig { m_id = "001", m_name = "test1", m_len = 10 }; InsertTable(tc1); //在插入一條 TestConfig tc2 = new TestConfig { m_id = "002", m_name = "test2", m_len = 20 }; InsertTable(tc2); //查詢數據 SelectTable(); //更新數據 tc2.m_name = "johnny"; tc2.m_len = 5; UpdateTable(tc2); SelectTable(); } void OnDestroy() { m_sqlite.Close(); } private void CreateTable() { StringBuilder sql = new StringBuilder(); sql.Append("create table "); sql.Append(TABLE_NAME); sql.Append("(ID VARCHAR(255) PRIMARY KEY,"); sql.Append("Name VARCHAR(255),"); sql.Append("Len INT);"); Excute(sql.ToString()); } private void InsertTable(TestConfig tc) { StringBuilder sql = new StringBuilder(); sql.Append("insert into "); sql.Append(TABLE_NAME); sql.Append(" values ('"); sql.Append(tc.m_id); sql.Append("','"); sql.Append(tc.m_name); sql.Append("',"); sql.Append(tc.m_len); sql.Append(");"); Excute(sql.ToString()); } private void SelectTable() { string sql = "select * from " + TABLE_NAME; foreach (var tc in m_sqlite.ExcuteSelectQuery<TestConfig>(sql)) { Debug.Log(tc); } } private void UpdateTable(TestConfig tc) { StringBuilder sql = new StringBuilder(); sql.Append("update "); sql.Append(TABLE_NAME); sql.Append(" set Name = '"); sql.Append(tc.m_name); sql.Append("', Len = "); sql.Append(tc.m_len); sql.Append(" where ID = '"); sql.Append(tc.m_id); sql.Append("';"); Excute(sql.ToString()); } private void Excute(string sql) { Debug.Log(sql.ToString()); m_sqlite.ExcuteQuery(sql); }}創建表、插入數據、更新數據這些都是直接執行sql語句沒什么可說的,但要注意的是在插入和更新多條數據時,使用事物會使效率提高很多。具體的方法就是在執行語句前調用BeginTrans()函數,執行后調用Commit()函數,如果有異常就回滾,參考ExcuteSelectQuery函數。
重點說一下查詢操作,也就是ExcuteSelectQuery這個函數。 一般情況下,在數據庫中獲得一行數據時,這一行數據應該是能跟某個類對象的屬性或字段一一對應的,也就是反序列化。但是反序列化的過程是繁瑣、重復的,如果數據庫中的每一個表都要去寫對應的反序列化函數真是太麻煩了(誰讓我們很懶呢╮(╯▽╰)╭)。為了簡化這個過程,定義一個特性ConfigFieldAttribute,其filedName 屬性就對應數據庫中的列名(當然也可以方便的將屬性名稱直接對應為數據庫中的列名),這樣一來,只需給對應屬性加上特性,即可完成反序列化,這里通過反射完成這一過程。
最后,Unity中運行結果:
此時,項目中會生成一個test.db文件,用數據庫可視化工具打開
大功告成?。。?/strong>最后放上自己的Unity工程。
第一篇文章,可能有些地方描述的不很清楚,如果有什么問題,歡迎大家指出?。。?/p>
新聞熱點
疑難解答