使用Qt5.12.9的QGraphicsItem來實現俄羅斯方塊,使用Sqlit3存儲數據來進行遊戲的回放,既然已經使用QT,就儘可能用其組件,重寫了原來的JSON封裝及數據庫操做接口實現。mysql
儘可能複用已經實現的代碼,因此只記錄了每一個方塊的形狀與姿態(旋轉次數)及最終位置。與真實遊戲的區別只是在於方塊的來源一個是隨機生成,一個是數據庫。記錄的ID我引用了原來使用的snowflake模型,但卻使用了QString類型,無它,只是長整型在編程的過程當中老是很差控制,莫名的被改爲科學計數法或溢出。數據庫操做使用C++對sqlit3庫進行封裝並結合Qjson實現ORM自動操做。Sqlit3的動態連接庫,在windows、linux、mac各平臺下須要從新編譯。linux
if(MSVC) #windows set(EnclibName Sqlit3.dll) elseif(APPLE) #mac set(EnclibName sqlite3.o) elseif(UNIX) #linux set(EnclibName libsqlite3.so) endif()
個人數據操做實現中的信息傳遞,都是以json對象形式進行的,封裝QJsonObject、QJsonArray等,提供方便的json操做接口,實現參數傳遞與SQL自動生成。個人實現Object是一等公民,Array是二等公民只做爲Qjson的一個子項。這樣既能知足應用又把設計大大的簡化了,規範使用,避免誤用。c++
class Qjson { private: QJsonObject* json; //真實的json對象,我只是對其封裝,簡化操做或轉化成簡單的操做 bool _IsObject_; //是不是正確的json對象,構造函數中對其進行判斷,表示json對象的有效性 public: Qjson(); Qjson(const Qjson& origin); //複製構造函數 QString operator[](QString key); //下標操做,取特定key對應的值,使用QString返回,能夠方便進行類型轉換 Qjson& operator = (const Qjson& origin); //賦值構造函數 bool HasMember(QString key) ; Qjson ExtendObject(Qjson obj); //合併兩個Object,第一個中同名key會被覆蓋 template<typename T> void AddValueBase(QString k, T v); //key - value 插入模板函數,基礎類型插入它就能搞定 void AddValueObjectsArray(string k, QVector<Qjson>& arr) ; //Array類型值插入 QString GetJsonString(); //取得json的字符串 void GetValueAndTypeByKey(QString key, QString* v, int* vType); //取值的真正操做函數 QStringList GetAllKeys(); //取得json對象的全部key,用於對象遍歷 bool IsObject(); private: QJsonObject* GetOriginRapidJson(); //取得真實的json對象,類內部使用 };
經實踐總結,數據庫操做有如下接口,百分之九十以上的需求就都能知足。git
class Idb { public: virtual Qjson select(QString tablename, Qjson& params, QStringList fields = QStringList(), int queryType = 1) = 0; virtual Qjson create(QString tablename, Qjson& params) = 0; virtual Qjson update(QString tablename, Qjson& params) = 0; virtual Qjson remove(QString tablename, Qjson& params) = 0; virtual Qjson querySql(QString sql, Qjson params = Qjson(), QStringList filelds = QStringList()) = 0; virtual Qjson execSql(QString sql) = 0; virtual Qjson insertBatch(QString tablename, QVector<Qjson> elements, QString constraint = "id") = 0; virtual Qjson transGo(QStringList sqls, bool isAsync = false) = 0; };
咱們的上層應用都是操做這個實現,這個類組合了一個Idb的具體實現,從而達到與具體的數據庫解耦的目的,能夠輕鬆的切換不一樣的數據庫實例。github
class DbBase { public: DbBase(QString connStr, QString dbType = "sqlit3") : connStr(connStr) { dbType.toLower(); if (dbType.compare("sqlit3") == 0) db = new Sqlit3::Sqlit3Db(connStr); else { throw "Db Type error or not be supported. "; } }; ... };
實現了Sqlit3的Idb接口,下面抽了幾個關鍵點進行一些說明:sql
數據鏈接使用了std::unique_ptr,在gcc下必定要引入 memory 頭文件,這個問題折騰了我很久,在windows下沒出問題,但在linux下一直報錯。數據庫
要保證送入底層的sql語句的編碼爲Utf-8,爲了保證對全部操做系統的支持,選擇使用QString::fromUtf8(szU8)來進行轉換。編程
Qjson ExecNoneQuerySql(QString aQuery) { Qjson rs = Utils::MakeJsonObjectForFuncReturn(STSUCCESS); sqlite3_stmt* stmt = NULL; sqlite3* handle = getHandle(); char * u8Query = Utils::UnicodeToU8(aQuery); //編碼轉換,函數中的主要功能由QString::fromUtf8完成 const int ret = sqlite3_prepare_v2(handle, u8Query, strlen(u8Query), &stmt, NULL); if (SQLITE_OK != ret) { QString errmsg = sqlite3_errmsg(getHandle()); rs.ExtendObject(Utils::MakeJsonObjectForFuncReturn(STDBOPERATEERR, errmsg)); } else { sqlite3_step(stmt); } sqlite3_finalize(stmt); qDebug() << "SQL: " << aQuery << endl; //日誌輸入使用未轉換的 return rs; }
遊戲記錄必須使用批量插入操做,不能一條一條插入。sqlit3的批量操做與mysql有些不一樣,用的是select ... union all,其實也只是按規定做好SQL語句拼接就能夠了。json
Qjson insertBatch(QString tablename, QVector<Qjson> elements, QString constraint) { QString sql = "insert into "; if (elements.empty()) { return Utils::MakeJsonObjectForFuncReturn(STPARAMERR); } else { QString keyStr = " ("; keyStr.append(elements[0].GetAllKeys().join(',')).append(" ) "); //取出參數第一個元素的全部key,組裝數據庫字段 for (size_t i = 0; i < elements.size(); i++) { QStringList keys = elements[i].GetAllKeys(); //取出參數的全部key,實現對json對象的遍歷 QString valueStr = " select "; for (size_t j = 0; j < keys.size(); j++) { valueStr.append("'").append(elements[i][keys[j]]).append("'"); if (j < keys.size() - 1) { valueStr.append(","); } } if (i < elements.size() - 1) { //拼接下一條記錄 valueStr.append(" union all "); } keyStr.append(valueStr); } sql.append(tablename).append(keyStr); } return ExecNoneQuerySql(sql); }
數據查詢使用的智能的ORM實現,具體方法請參照前文《c++關係數據庫訪問通用接口設計》segmentfault
回放設置了幾個參數
項目採用cmake組織,請安裝cmake3.10以上版本。下面腳本是windows下基於MSVC的,其它操做系統上基本相似,或者使用qtcreator打開進行操做。
cmake -A win32 -Bbuild . cd build cmake --build . --config Release
注:本項目採用方案能跨平臺運行,已經適配過windows,linux,mac。
源代碼:
https://gitee.com/zhoutk/qtetris.git
或
https://gitee.com/zhoutk/qtdemo/tree/master/tetrisGraphicsItem
或
https://github.com/zhoutk/qtDemo/tree/master/tetrisGraphicsItem