2014-05-04更新html
SqliteDatabase.cs這個文件的初始方法有問題,具體是若是指定URL已經存在了DB文件,就不會從新覆蓋DB文件。android
這致使咱們修改以後的DB文件沒法產生效果。ios
本人的解決辦法是在遊戲啓動的界面,經過對比本地的Resources目錄下的文件A,和玩家核心數據B裏面的數據庫版本號,sql
若是A>B,則斷定本地的DB文件版本較老,須要更新。數據庫
具體代碼請到目錄SQLite篇下下載c#
2014-04-30更新網絡
剔除了使用網絡上爛大街的SQLite使用方法(緣由android下沒法讀取數據),使用libSQLite3.so,經過DLLImport,在C#代碼裏直接調用C接口數據結構
這種原生調用SQLite的方式,我在pc、android上親測無誤,ios沒測過,可是stackoverflow上有兄弟試過,沒問題。園子的朋友若是能夠測IOS的,歡迎提供結果app
基本思路,遊戲基礎配置數據,好比怪物的屬性、裝備模板屬性、關卡怪物等,使用SQLite(Unity插件SQLiteUnityKit-GitHub地址,推薦客戶端SQLite Expert Personal 3),管理方便框架
玩家核心數據(屬性、裝備、技能)使用JSON格式,加密保存在Application.persistentDataPath路徑裏,避免每次升級被覆蓋
1 litJson.dll放在Plugins目錄下
2 libsqlite3.so文件放到 Assets/Plugins/Android目錄下
3 自定義的SQLite DB數據文件放到 Assets/StreamingAssets目錄下
將準備好的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的例子,你們能夠下載導入
原先是使用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
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 ("取消"); } }