前言:
一般游戲需要在手機上記錄一些簡單的信息,用來保存游戲的進度,玩家的分數等。SQLite作為輕量級、跨平台的關系型數據庫,相當適合用於游戲數據的存儲。
由於沒有加密,有安全性問題,數據上還需要自己做些加密驗證等。
封裝效果
為了達到簡單易用的效果,封裝后接口大致如下。通過僅通過調用GetData和SetData來獲取數據及存儲數據。
class CDataMgr { public: static CDataMgr* getInstance(); static void destroyInstance(); const SSqlData& GetData(enDataType nType){ return m_mKeyValue[nType]; } void SetData(enDataType nType, SSqlData& data); private: void GetSqlData(enSqlTableType nSqlType, std::vector<enDataType>& vDataType); void SaveSqlTable(enSqlTableType nSqlType); void OpenSql(enSqlTableType nType); // 打開數據庫操作 void CloseSql(); // 關閉數據庫操作 CDataMgr(); ~CDataMgr(); std::map<enDataType, SSqlData> m_mKeyValue; // 程序當前的值 sqlite3 * m_pSqlData; // 數據庫指針 };
輔助數據結構
Get及Set得到的數據需要知道類型且可以得到正常的值,同時還需要設置在sqlite中字段名字等,因此需要幾個輔助的結構體來存儲數據類型等。如下:
// 數據枚舉 enum class enDataType { SOUND, // 聲音數據 0x01 音樂 0x02 音效 (1:開啟,0:關閉) PLAYERID, // 上次GAMECENTER玩家數據 ISGUIDED, // 是否已經引導過 LASTTIME, // 上次時間 PLAYER_INDEX, // 每個的索引 MONEY, // 金幣 RECORD, // 記錄 }; // Sqlite表格類型 enum class enSqlTableType { DEFAULT, // 默認數據存儲 聲音等 PLAYER, // 玩家數據存儲 金幣 }; // 存儲數據類型 enum class enSqlDataType { NONE, // 沒有數據 INT, // int類型 LONG, // long類型 BOOL, // bool 類型 STRING, // string 類型 }; // 數據結構體 struct SSqlData { public: SSqlData() { memset(&udata, 0, sizeof(udata)); } SSqlData(enSqlTableType ntable, enSqlDataType ntype, std::string name) :nSqlTableType(ntable), nSqlDataType(ntype), sSqlDataName(name) { memset(&udata, 0, sizeof(udata)); } // 實際數據 union UData { int _intData; long _longData; bool _boolData; }; UData udata; std::string _strData; enSqlTableType nSqlTableType; // 數據表格 enSqlDataType nSqlDataType; // 數據庫中數據類型 std::string sSqlDataName; // 數據庫中名字 };
簡單說明:
1、SSqlData是實際設置獲取的數據,里面包含有Sqlite的字段名字,數據類型及實際保存數據等。
2、enSqlDataType 是數據存儲類型的枚舉。
3、enSqlTableType 是數據表格的枚舉類型,一般游戲至少有2張表格,分別用來存儲每個玩家數據(分數)及所有玩家共同的數據(聲音)。
4、enDataType 是列舉了所有的游戲存儲值的枚舉,用來索引SSqlData的,游戲通過enDataType 來獲取SSqlData。
數據初始化描述
游戲初始化時在DataMgr的構造函數中需要對所有的枚舉值進行描述,設置所屬表格及字段類型,字段名等。同時還需要從Sqlite取出默認的數據,如下:
CDataMgr::CDataMgr() :m_pSqlData(0) { // 初始化key-value m_mKeyValue[enDataType::SOUND] = SSqlData(enSqlTableType::DEFAULT, enSqlDataType::INT, "sound"); m_mKeyValue[enDataType::PLAYERID] = SSqlData(enSqlTableType::DEFAULT, enSqlDataType::STRING, "playerId"); m_mKeyValue[enDataType::ISGUIDED] = SSqlData(enSqlTableType::DEFAULT, enSqlDataType::BOOL, "isGuided"); m_mKeyValue[enDataType::LASTTIME] = SSqlData(enSqlTableType::DEFAULT, enSqlDataType::LONG, "lastTime"); m_mKeyValue[enDataType::MONEY] = SSqlData(enSqlTableType::PLAYER, enSqlDataType::LONG, "money"); m_mKeyValue[enDataType::RECORD] = SSqlData(enSqlTableType::PLAYER, enSqlDataType::LONG, "record"); m_mKeyValue[enDataType::PLAYER_INDEX] = SSqlData(enSqlTableType::PLAYER, enSqlDataType::STRING, "playerId"); std::vector<enDataType> vDataType; vDataType.push_back(enDataType::PLAYERID); vDataType.push_back(enDataType::SOUND); vDataType.push_back(enDataType::LASTTIME); vDataType.push_back(enDataType::ISGUIDED); GetSqlData(enSqlTableType::DEFAULT, vDataType); // 獲取數據庫記錄 vDataType.clear(); vDataType.push_back(enDataType::MONEY); vDataType.push_back(enDataType::RECORD); GetSqlData(enSqlTableType::PLAYER, vDataType); // 獲取數據庫記錄 }
數據存儲
為了節省效率,在實際SetData及GetData並不會從Sqlite中存取,而是從緩存的 m_mKeyValue 中取值設置。然而當玩家角色發生變化及游戲結束就必須及時對玩家數據進行存儲。
因此在析構函數及SetData中有如下操作:
CDataMgr::~CDataMgr() { SaveSqlTable(enSqlTableType::DEFAULT); SaveSqlTable(enSqlTableType::PLAYER); } void CDataMgr::SetData(enDataType nType, SSqlData& data) { // 玩家角色變化更新玩家數據 if (enDataType::PLAYERID == nType) { SaveSqlTable(enSqlTableType::PLAYER); m_mKeyValue[nType] = data; std::vector<enDataType> vDataType; vDataType.push_back(enDataType::MONEY); vDataType.push_back(enDataType::RECORD); GetSqlData(enSqlTableType::PLAYER, vDataType); } else { m_mKeyValue[nType] = data; } }
測試數據:
SSqlData guid_data = CDataMgr::getInstance()->GetData(enDataType::ISGUIDED); SSqlData last_data = CDataMgr::getInstance()->GetData(enDataType::LASTTIME); SSqlData player_data = CDataMgr::getInstance()->GetData(enDataType::PLAYERID); SSqlData record_data = CDataMgr::getInstance()->GetData(enDataType::RECORD); CCLOG("guid_data:%d", guid_data.udata._boolData ? 1 : 0); CCLOG("lasttime:%ld", last_data.udata._longData); CCLOG("player:%s", player_data._strData.c_str()); CCLOG("record:%ld", record_data.udata._longData); guid_data.udata._boolData = true; player_data._strData = "xxxxxxxxxxzzzzzzzzzz___1"; record_data.udata._longData = 100; CDataMgr::getInstance()->SetData(enDataType::ISGUIDED, guid_data); // 通用引導數據變化 CDataMgr::getInstance()->SetData(enDataType::PLAYERID, player_data); // 登錄玩家變化 CDataMgr::getInstance()->SetData(enDataType::RECORD, record_data); // 玩家記錄變化 record_data = CDataMgr::getInstance()->GetData(enDataType::RECORD); CCLOG("record__1 :%ld", record_data.udata._longData); player_data._strData = "xxxxxxxxxxzzzzzzzzzz_____2"; CDataMgr::getInstance()->SetData(enDataType::PLAYERID, player_data); // 登錄玩家變化 record_data = CDataMgr::getInstance()->GetData(enDataType::RECORD); CCLOG("record__2 :%ld", record_data.udata._longData);
結果如下圖:

當然,也可以通過sqliteadmin之類的軟件直接打開db文件查看數據,因為沒加密~~~~~~~~~~
完整代碼地址:https://github.com/mydishes/cocos2dx-Ex/tree/master/SqliteMgr
