Unity3D游戲開發之SQLite讓數據庫開發更簡單


一、什么是SQLite?

SQLite是一款輕型的數據庫,是遵守ACID的關系型數據庫管理系統,它包括在一個相對小的C庫中,以嵌入式作為它的設計目標,它占用資源很的低,因此適合在嵌入式設備如Android、Ruby on Rails等中使用。它能夠支持Windows/Linux/Unix等等主流的操作系統。同一時候能夠跟和C、C++、Ruby、Python、C#、PHP、Java等編程語言相結合。SQLite是一個以文件形式存在的關系型數據庫,雖然無法實現分布式和橫向擴展,但是作為一個輕量級的嵌入式數據庫。它不須要系統提供服務支持,通過SDK直接操作文件避免了對數據庫維護的相關事務,從這個角度來講它是一個出色的數據庫。

二、為什么要選擇SQLite

好了,在了解了SQLite后,我們來了解下SQLite有哪些讓我們心動的特性,或者說我們為什么要選擇SQLite,由於在這個世界上我們有太多的數據庫能夠選擇。諸如Oracle、MySQL、SQLServer、DB2、NoSQL、MongoDB等等:

  • ACID事務
  • 零配置 – 無需安裝和管理配置
  • 儲存在單一磁盤文件里的一個完整的數據庫
  • 數據庫文件能夠在不同字節順序的機器間自由的共享
  • 支持數據庫大小至2TB
  • 足夠小, 大致13萬行C代碼, 4.43M
  • 比一些流行的數據庫在大部分普通數據庫操作要快—SQLite讀寫效率如此之高,會使用其它數據庫的理由是?
  • 簡單, 輕松的API
  • 包括TCL綁定, 同一時候通過Wrapper支持其它語言的綁定
  • 良好凝視的源代碼, 而且有着90%以上的測試覆蓋率
  • 獨立: 沒有額外依賴
  • 源代碼全然的開源, 你能夠用於不論什么用途, 包括出售它
  • 支持多種開發語言,C, C++, PHP, Perl, Java, C#,Python, Ruby等

三、Unity3D中的SQLite

在Unity3D中使用SQLite。我們首先要明確這樣一件事情。即我們這里的使用的SQLite並不是是通常意義上的SQLite.NET,而是經過移植后的Mono.Data.Sqlite。由於Unity3D基於Mono,因此使用移植后的Mono.Data.Sqlite能夠降低我們的項目在不同平台上出現各種各樣的問題。

在Unity3D中使用的SQLite以Mono.Data.Sqlite.dll即動態鏈接庫的形式給出,因此我們須要將這個文件放置在項目文件夾下的Plugins文件夾中,此外我們須要System.Data.dll或者Mono.Data.dll這兩個文件加入到Plugins文件夾中,由於我們須要的部分數據相關的API或者類都定義在這兩個文件其中,這些文件能夠從這里直接下載。

PS:博主注意到在網上有使用Mono.Data.SQLiteClient.dll這個庫實如今Unity3D操作SQLite數據庫的相關文章。博主大概看了下,感覺和使用Mono.Data.Sqlite.dll這個庫大同小異,大家喜歡哪個就用哪個吧!哈哈!博主在開源社區找到一個版本號庫。據說能夠同一時候支持.NET和Mono,假設大家感興趣歡迎大家去測試啊,哈哈!

在正式開始寫代碼前。我們首先來回想下通常情況下數據庫讀寫的基本流程吧!

  • 定義數據庫連接字符串(ConnectionString)完畢數據庫連接的構造,建立或者打開一個數據庫。
  • 定義相關的SQL命令(Command)通過這些命令實現對數據庫的添加、刪除、更新、讀取四種基本功能。 
  • 在完畢各種數據庫操作后及時關閉數據庫連接。解除對數據庫的連接和引用。

SQLite作為一款優秀的數據庫,在為其編寫數據庫相關代碼時相同遵循這種流程,考慮到對數據庫的添加、刪除、更新、讀取四種操作具有相似性和統一性,因此在動手寫Unity3D腳本前。首先讓我們來編寫一個SQLite的輔助類SQLiteHelper.cs。該類代碼定義例如以下:

using UnityEngine;
using System.Collections;
using Mono.Data.Sqlite;
using System;

public class SQLiteHelper
{
    /// <summary>
    /// 數據庫連接定義
    /// </summary>
    private SqliteConnection dbConnection;

    /// <summary>
    /// SQL命令定義
    /// </summary>
    private SqliteCommand dbCommand;

    /// <summary>
    /// 數據讀取定義
    /// </summary>
    private SqliteDataReader dataReader;

    /// <summary>
    /// 構造函數    
    /// </summary>
    /// <param name="connectionString">數據庫連接字符串</param>
    public SQLiteHelper(string connectionString)
    {
        try{
            //構造數據庫連接
            dbConnection=new SqliteConnection(connectionString);
            //打開數據庫
            dbConnection.Open();
        }catch(Exception e)
        {
            Debug.Log(e.Message);
        }
    }

    /// <summary>
    /// 執行SQL命令
    /// </summary>
    /// <returns>The query.</returns>
    /// <param name="queryString">SQL命令字符串</param>
    public SqliteDataReader ExecuteQuery(string queryString)
    {
        dbCommand = dbConnection.CreateCommand();
        dbCommand.CommandText = queryString;
        dataReader = dbCommand.ExecuteReader();
        return dataReader;
    }

    /// <summary>
    /// 關閉數據庫連接
    /// </summary>
    public void CloseConnection()
    {
        //銷毀Command
        if(dbCommand != null){
            dbCommand.Cancel();
        }
        dbCommand = null;

        //銷毀Reader
        if(dataReader != null){
            dataReader.Close();
        }
        dataReader = null;

        //銷毀Connection
        if(dbConnection != null){
            dbConnection.Close();
        }
        dbConnection = null;
    }

    /// <summary>
    /// 讀取整張數據表
    /// </summary>
    /// <returns>The full table.</returns>
    /// <param name="tableName">數據表名稱</param>
    public SqliteDataReader ReadFullTable(string tableName)
    {
        string queryString = "SELECT * FROM " + tableName;
        return ExecuteQuery (queryString);
    }

    /// <summary>
    /// 向指定數據表中插入數據
    /// </summary>
    /// <returns>The values.</returns>
    /// <param name="tableName">數據表名稱</param>
    /// <param name="values">插入的數值</param>
    public SqliteDataReader InsertValues(string tableName,string[] values)
    {
        //獲取數據表中字段數目
        int fieldCount=ReadFullTable(tableName).FieldCount;
        //當插入的數據長度不等於字段數目時引發異常
        if(values.Length!=fieldCount){
            throw new SqliteException("values.Length!=fieldCount");
        }

        string queryString = "INSERT INTO " + tableName + " VALUES (" + values[0];
        for(int i=1; i<values.Length; i++)
        {
            queryString+=", " + values[i];
        }
        queryString += " )";
        return ExecuteQuery(queryString);
    }

    /// <summary>
    /// 更新指定數據表內的數據
    /// </summary>
    /// <returns>The values.</returns>
    /// <param name="tableName">數據表名稱</param>
    /// <param name="colNames">字段名</param>
    /// <param name="colValues">字段名相應的數據</param>
    /// <param name="key">關鍵字</param>
    /// <param name="value">關鍵字相應的值</param>
    public SqliteDataReader UpdateValues(string tableName,string[] colNames,string[] colValues,string key,string operation,string value)
    {
        //當字段名稱和字段數值不正確應時引發異常
        if(colNames.Length!=colValues.Length) {
            throw new SqliteException("colNames.Length!=colValues.Length");
        }

        string queryString = "UPDATE " + tableName + " SET " + colNames[0] + "=" + colValues[0];
        for(int i=1; i<colValues.Length; i++) 
        {
            queryString+=", " + colNames[i] + "=" + colValues[i];
        }
        queryString += " WHERE " + key + operation + value;
        return ExecuteQuery(queryString);
    }

    /// <summary>
    /// 刪除指定數據表內的數據
    /// </summary>
    /// <returns>The values.</returns>
    /// <param name="tableName">數據表名稱</param>
    /// <param name="colNames">字段名</param>
    /// <param name="colValues">字段名相應的數據</param>
    public SqliteDataReader DeleteValuesOR(string tableName,string[] colNames,string[] operations,string[] colValues)
    {
        //當字段名稱和字段數值不正確應時引發異常
        if(colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length) {
            throw new SqliteException("colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length");
        }

        string queryString = "DELETE FROM " + tableName + " WHERE " + colNames[0] + operations[0] + colValues[0];
        for(int i=1; i<colValues.Length; i++) 
        {
            queryString+="OR " + colNames[i] + operations[0] + colValues[i];
        }
        return ExecuteQuery(queryString);
    }

    /// <summary>
    /// 刪除指定數據表內的數據
    /// </summary>
    /// <returns>The values.</returns>
    /// <param name="tableName">數據表名稱</param>
    /// <param name="colNames">字段名</param>
    /// <param name="colValues">字段名相應的數據</param>
    public SqliteDataReader DeleteValuesAND(string tableName,string[] colNames,string[] operations,string[] colValues)
    {
        //當字段名稱和字段數值不正確應時引發異常
        if(colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length) {
            throw new SqliteException("colNames.Length!=colValues.Length || operations.Length!=colNames.Length || operations.Length!=colValues.Length");
        }

        string queryString = "DELETE FROM " + tableName + " WHERE " + colNames[0] + operations[0] + colValues[0];
        for(int i=1; i<colValues.Length; i++) 
        {
            queryString+=" AND " + colNames[i] + operations[i] + colValues[i];
        }
        return ExecuteQuery(queryString);
    }

    /// <summary>
    /// 創建數據表
    /// </summary> +
    /// <returns>The table.</returns>
    /// <param name="tableName">數據表名</param>
    /// <param name="colNames">字段名</param>
    /// <param name="colTypes">字段名類型</param>
    public SqliteDataReader CreateTable(string tableName,string[] colNames,string[] colTypes)
    {
        string queryString = "CREATE TABLE " + tableName + "( " + colNames [0] + " " + colTypes [0];
        for (int i=1; i<colNames.Length; i++) 
        {
            queryString+=", " + colNames[i] + " " + colTypes[i];
        }
        queryString+= "  ) ";
        return ExecuteQuery(queryString);
    }

    /// <summary>
    /// Reads the table.
    /// </summary>
    /// <returns>The table.</returns>
    /// <param name="tableName">Table name.</param>
    /// <param name="items">Items.</param>
    /// <param name="colNames">Col names.</param>
    /// <param name="operations">Operations.</param>
    /// <param name="colValues">Col values.</param>
    public SqliteDataReader ReadTable(string tableName,string[] items,string[] colNames,string[] operations, string[] colValues)
    {
        string queryString = "SELECT " + items [0];
        for (int i=1; i<items.Length; i++) 
        {
            queryString+=", " + items[i];
        }
        queryString += " FROM " + tableName + " WHERE " + colNames[0] + " " +  operations[0] + " " + colValues[0];
        for (int i=0; i<colNames.Length; i++) 
        {
            queryString+=" AND " + colNames[i] + " " + operations[i] + " " + colValues[0] + " ";
        }
        return ExecuteQuery(queryString);
    }
}

  

SQLiteHelper類主要實現了數據庫、數據表的創建以及數據表中記錄的添加、刪除、更新、讀取四種基本功能。該類最初由國外的Unity3D開發人員公布在Unity3D官方論壇,后來經宣雨松使用C#進行重寫。我在此基礎上進行了完好。再此對兩位大神的無私付出表示感謝。

這里要說明的有三點:

  • 一、在Unity3D編輯器下生成數據庫文件(.db)默認位於和Assets文件夾同級的位置。即項目的project文件夾中。我們能夠通過改動路徑在改變數據庫文件的存儲位置,詳細來講: 
    Windows平台:data source=Application.dataPath/數據庫名稱.db 
    IOS平台:data source=Application.persistentDataPath/數據庫名稱.db 
    Android平台:URL=file:Application.persistentDataPath/數據庫名稱.db(我想說Android平台就是個奇葩。搞什么特殊化嘛)

  • 二、確保Unity3D編輯器中的.NET版本號和MonoDevelop中的.NET版本號都為2.0版本號,在Unity3D中打包導出的程序可能不會保留數據庫文件,因此須要手動將數據庫文件復制到相應的位置,當然更加合理的方案是將數據庫文件存放到StreamingAssets文件夾下,然后在第一次載入游戲的時候將數據庫文件復制到相應平台上的存放位置。

  • 三、在使用InsertValues方法時請參考SQLite中字段類型與C#中數據類型的相應關系,博主眼下測試了int類型和string類型都沒有什么問題。很多其它類型的數據請大家自行測試然后告訴博主測試的結果。假設大家有興趣擴展這個輔助類的話能夠自行去擴展哦,嘿嘿!

好了。千呼萬喚始出來的時候到了,以下我們以一個實例來完畢今天的項目解說,由於我們已經定義好了SQLite的輔助類,因此我們能夠高速地編寫出以下的腳本代碼:

using UnityEngine;
using System.Collections;
using System.IO;
using Mono.Data.Sqlite;

public class SQLiteDemo : MonoBehaviour 
{
    /// <summary>
    /// SQLite數據庫輔助類
    /// </summary>
    private SQLiteHelper sql;

    void Start () 
    {
        //創建名為sqlite4unity的數據庫
        sql = new SQLiteHelper("data source=sqlite4unity.db");

        //創建名為table1的數據表
        sql.CreateTable("table1",new string[]{"ID","Name","Age","Email"},new string[]{"INTEGER","TEXT","INTEGER","TEXT"});

        //插入兩條數據
        sql.InsertValues("table1",new string[]{"'1'","'張三'","'22'","'Zhang3@163.com'"});
        sql.InsertValues("table1",new string[]{"'2'","'李四'","'25'","'Li4@163.com'"});

        //更新數據。將Name="張三"的記錄中的Name改為"Zhang3"
        sql.UpdateValues("table1", new string[]{"Name"}, new string[]{"'Zhang3'"}, "Name", "=", "'張三'");

        //插入3條數據
        sql.InsertValues("table1",new string[]{"3","'王五'","25","'Wang5@163.com'"});
        sql.InsertValues("table1",new string[]{"4","'王五'","26","'Wang5@163.com'"});
        sql.InsertValues("table1",new string[]{"5","'王五'","27","'Wang5@163.com'"});

        //刪除Name="王五"且Age=26的記錄,DeleteValuesOR方法相似
        sql.DeleteValuesAND("table1", new string[]{"Name","Age"}, new string[]{"=","="}, new string[]{"'王五'","'26'"});

        //讀取整張表
        SqliteDataReader reader = sql.ReadFullTable ("table1");
        while(reader.Read()) 
        {
            //讀取ID
            Debug.Log(reader.GetInt32(reader.GetOrdinal("ID")));
            //讀取Name
            Debug.Log(reader.GetString(reader.GetOrdinal("Name")));
            //讀取Age
            Debug.Log(reader.GetInt32(reader.GetOrdinal("Age")));
            //讀取Email
            Debug.Log(reader.GetString(reader.GetOrdinal("Email")));
        }

        //讀取數據表中Age>=25的全部記錄的ID和Name
        reader = sql.ReadTable ("table1", new string[]{"ID","Name"}, new string[]{"Age"}, new string[]{">="}, new string[]{"'25'"});
        while(reader.Read()) 
        {
            //讀取ID
            Debug.Log(reader.GetInt32(reader.GetOrdinal("ID")));
            //讀取Name
            Debug.Log(reader.GetString(reader.GetOrdinal("Name")));
        }

        //自己定義SQL,刪除數據表中全部Name="王五"的記錄
        sql.ExecuteQuery("DELETE FROM table1 WHERE NAME='王五'");

        //關閉數據庫連接
        sql.CloseConnection();
    }
}

  

在上面的代碼中我們是在Start方法中創建了數據庫和數據表,然而在實際使用中我們須要推斷數據庫和數據表是否存在。因此假設你使用這段腳本提示錯誤信息,請確保數據庫和數據表是否已經存在。

好了,以下的截圖展示了程序執行的結果:

數據庫效果演示

Unity3D效果展示

作為一個強大的數據庫怎么能沒有圖形化的數據庫管理工具呢?所以這里博主向大家推薦一個免安裝的小工具SqliteStudio,使用這個工具能夠幫助我們方便地管理Sqlite數據庫里的數據,這樣是不是比較方便呢?哈哈!

這個工具能夠從這里下載哦。

SQLiteStudio界面演示

好了,今天的內容就是這樣了,為了寫這篇文章花了三個晚上准備。希望大家喜歡啊!

假設大家認為這篇文章實用,請繼續關注我的博客。我是秦元培。我的博客地址是http://blog.csdn.net/qinyuanpei

2015年11月3日更新內容:在不同的平台上數據庫的存儲位置是不同的。在這里給出一個參考的路徑。希望大家在處理移動端的時候注意這些問題啊!

        //各平台下數據庫存儲的絕對路徑(通用)
        //PC:sql = new SQLiteHelper("data source=" + Application.dataPath + "/sqlite4unity.db");
        //Mac:sql = new SQLiteHelper("data source=" + Application.dataPath + "/sqlite4unity.db");
        //Android:sql = new SQLiteHelper("URI=file:" + Application.persistentDataPath + "/sqlite4unity.db");
        //iOS:sql = new SQLiteHelper("data source=" + Application.persistentDataPath + "/sqlite4unity.db");

        //PC平台下的相對路徑
        //sql = new SQLiteHelper("data source="sqlite4unity.db");
        //編輯器:Assets/sqlite4unity.db
        //編譯后:和AppName.exe同級的文件夾下,這里比較奇葩
        //當然能夠用更任意的方式sql = new SQLiteHelper("data source="D://SQLite//sqlite4unity.db");
        //確保路徑存在就可以否則會錯誤發生

        //假設是事先創建了一份數據庫
        //能夠將這個數據庫放置在StreamingAssets文件夾下然后再復制到
        //Application.persistentDataPath + "/sqlite4unity.db"路徑就可以

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM