Unity本地數據存儲---Sqlite和JSON


 

2014-05-04更新

SqliteDatabase.cs這個文件的初始方法有問題,具體是如果指定URL已經存在了DB文件,就不會重新覆蓋DB文件。

這導致我們修改之后的DB文件無法產生效果。

本人的解決辦法是在游戲啟動的界面,通過對比本地的Resources目錄下的文件A,和玩家核心數據B里面的數據庫版本號,

如果A>B,則判定本地的DB文件版本較老,需要更新。

具體代碼請到目錄SQLite篇下下載

 

 

2014-04-30更新

剔除了使用網絡上爛大街的SQLite使用方法(原因android下無法讀取數據),使用libSQLite3.so,通過DLLImport,在C#代碼里直接調用C接口

這種原生調用SQLite的方式,我在pc、android上親測無誤,ios沒測過,但是stackoverflow上有兄弟試過,沒問題。園子的朋友如果可以測IOS的,歡迎提供結果

 

基本思路,游戲基礎配置數據,比如怪物的屬性、裝備模板屬性、關卡怪物等,使用SQLite(Unity插件SQLiteUnityKit-GitHub地址,推薦客戶端SQLite Expert Personal 3),管理方便

玩家核心數據(屬性、裝備、技能)使用JSON格式,加密保存在Application.persistentDataPath路徑里,避免每次升級被覆蓋

插件本地下載地址

Sqlite框架

litJson

 

准備工作

 1 litJson.dll放在Plugins目錄下

 2 libsqlite3.so文件放到 Assets/Plugins/Android目錄下

 3 自定義的SQLite DB數據文件放到 Assets/StreamingAssets目錄下

SQLite篇

 將准備好的DB數據文件拷貝到Assets/StreamingAssets

 把SQLiteUnityKit GitHub下載的壓縮包里的 DataTable.cs、SqliteDatabase.cs,拷貝到項目中的任意位置

 最終項目結構看起來是這樣子的

 SQLiteUnityKit框架用Dictionary數據結構模擬了DataTable,DataRow,因此我們執行查詢語句的時候,返回的是DataTable,就像平時使用ado.net提供的查詢模式一樣。

 調用方式

SqliteDatabase sqlDB = new SqliteDatabase(“config.db”);
string query = “INSERT INTO User(UserName) VALUES( ‘Santiago’)”;
sqlDB.ExecuteNonQuery(query);

我做了個unitypackge的例子,大家可以下載導入

Json篇

原先是使用XML格式來存儲數據的,因為XML跨平台,但是Json同樣也可以做到,加上有LitJson這個格式轉化利器,因此,本地文件存儲格式,本文以Json為例。

關於數據加密

使用c#提供的加密類即可,自己定義秘鑰

using System.Security.Cryptography;
using System.Text;

public class GlobalDataHelper
{
        private const string DATA_ENCRYPT_KEY = "a234857890654c3678d77234567890O2";
        private static RijndaelManaged _encryptAlgorithm = null;

        public static RijndaelManaged DataEncryptAlgorithm ()
        {
                _encryptAlgorithm = new RijndaelManaged ();
                _encryptAlgorithm.Key = Encoding.UTF8.GetBytes (DATA_ENCRYPT_KEY);
                _encryptAlgorithm.Mode = CipherMode.ECB;
                _encryptAlgorithm.Padding = PaddingMode.PKCS7;

                return _encryptAlgorithm;
        }
}

 

關於破解版軟件

安卓機子上泛濫各種XXX破解版,關於破解版的問題,我們可以通過Unity提供的唯一機器ID,在寫入玩家數據的時候,將其一並寫入到數據中去,在讀取數據之后,對比該ID和本機ID,如果不一致,則認為是破解版

SystemInfo.deviceUniqueIdentifier

本例子是以基礎配置數據為例,因此代碼中不提供該功能。

 

關於避免更新之后,玩家存檔被覆蓋

Unity提供了一個只讀路徑,放在該路徑下的文件,不會被軟件更新所影響。

Application.persistentDataPath
 
        

Json Helper類~~

using System.Security.Cryptography;
using System.Text;
using System;
using System.IO;
using LitJson;

public class DataStoreProcessor
{

        private static DataStoreProcessor _dataStoreProcessor = null;
    
        public static DataStoreProcessor SharedInstance {
                get {
                        if (_dataStoreProcessor == null)
                                _dataStoreProcessor = new DataStoreProcessor ();

                        return _dataStoreProcessor;
                }
        }

        /// <summary>
        /// 加密數據    
        /// </summary>
        /// <returns>The data.</returns>
        /// <param name="dataToEncrypt">Data to encrypt.</param>
        public string EncryptData (string dataToEncrypt)
        {
                //給明文加密用GetBytes
                byte[] dataToEncryptArray = Encoding.UTF8.GetBytes (dataToEncrypt);
                byte[] dataAfterEncryptArray = GlobalDataHelper.DataEncryptAlgorithm().CreateEncryptor ()
            .TransformFinalBlock (dataToEncryptArray, 0, dataToEncryptArray.Length);
        
                return Convert.ToBase64String (dataAfterEncryptArray, 0, dataAfterEncryptArray.Length);
        }
    
        /// <summary>
        /// 解密數據 
        /// </summary>
        /// <returns>The data.</returns>
        /// <param name="dataToDecrypt">Data to decrypt.</param>
        public string DecryptData (string dataToDecrypt)
        {
                //給密文解密用FromBase64String
                byte[] dataToDecryptArray = Convert.FromBase64String (dataToDecrypt);
                byte[] dataAfterDecryptArray = GlobalDataHelper.DataEncryptAlgorithm().CreateDecryptor ()
            .TransformFinalBlock (dataToDecryptArray, 0, dataToDecryptArray.Length);
        
                return Encoding.UTF8.GetString (dataAfterDecryptArray);
        }

        /// <summary>
        /// 數據保存
        /// </summary>
        /// <param name="tobject">Tobject.</param>
        /// <param name="path">Path.</param>
        /// <typeparam name="T">The 1st type parameter.</typeparam>
        public void Save (Object tobject, string path, bool isEncrypt=true)
        {
                string serializedString = JsonMapper.ToJson (tobject);

                using (StreamWriter sw = File.CreateText(path)) {
                        if (isEncrypt)
                                sw.Write (EncryptData (serializedString));
                        else
                                sw.Write (serializedString);
                }
        }

        /// <summary>
        /// 載入數據
        /// </summary>
        /// <param name="path">Path.</param>
        /// <typeparam name="T">The 1st type parameter.</typeparam>
        public T Load<T> (string path, bool isEncrypt=true)
        {
                if (File.Exists (path) == false)
                        return default(T);
        
                using (StreamReader sr = File.OpenText(path)) {
                        string stringEncrypt = sr.ReadToEnd ();
            
                        if (string.IsNullOrEmpty (stringEncrypt))
                                return default(T);
            
                        if (isEncrypt)
                                return  JsonMapper.ToObject<T> (DecryptData (stringEncrypt));
                        else
                                return JsonMapper.ToObject<T> (stringEncrypt);
                }
        }
}

調用方式

 下面的代碼將提供了一個自定義窗體,允許開發者自行定義用戶在等待界面時,顯示本地配置好的文字

 按照道理,這種游戲基礎配置類的應該使用Sql方式來進行數據交互,本文僅僅是為了進行功能的演示。

只有玩家數據,才使用本地文件存儲的方式,存儲在永久的路徑里面

using UnityEngine;
using System.Collections.Generic;
using UnityEditor;

public class LoadingDataConfigWindow : ScriptableWizard
{
    public List<string> NotifyString;
    //改成 Application.persistentDataPath永久存儲
    private readonly string LOADING_DATA_CONFIG_URL = Application.dataPath + @"/Resources/Setting/LoadNotify.data";


    public LoadingDataConfigWindow()
    {
        NotifyString = DataStoreProcessor.SharedInstance.Load<List<string>>(LOADING_DATA_CONFIG_URL,false);
    }

    [MenuItem ("GameObject/Data Setting/Loading text")]
    static void CreateWizard ()
    {
        LoadingDataConfigWindow window =    DisplayWizard<LoadingDataConfigWindow> ("配置登陸提示文字", "確認", "取消");
        window.minSize = new Vector2(1024,768);
    }
    
    // This is called when the user clicks on the Create button.  
    void OnWizardCreate ()
    {  
        DataStoreProcessor.SharedInstance.Save(NotifyString,LOADING_DATA_CONFIG_URL,false);
        Debug.Log(string.Format(" 保存成功,共計錄入 {0} 數據",NotifyString.Count));
    }  
    
    // Allows you to provide an action when the user clicks on the   
    // other button "Apply".  
    void OnWizardOtherButton ()
    {  
        Debug.Log ("取消");
    }  
}

 


免責聲明!

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



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