前言:
一般游戏需要在手机上记录一些简单的信息,用来保存游戏的进度,玩家的分数等。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