QT5寫Tetris之使用Sqlite3實現遊戲回放

背景

使用Qt5.12.9的QGraphicsItem來實現俄羅斯方塊,使用Sqlit3存儲數據來進行遊戲的回放,既然已經使用QT,就儘可能用其組件,重寫了原來的JSON封裝及數據庫操做接口實現。mysql

思路

儘可能複用已經實現的代碼,因此只記錄了每一個方塊的形狀與姿態(旋轉次數)及最終位置。與真實遊戲的區別只是在於方塊的來源一個是隨機生成,一個是數據庫。記錄的ID我引用了原來使用的snowflake模型,但卻使用了QString類型,無它,只是長整型在編程的過程當中老是很差控制,莫名的被改爲科學計數法或溢出。數據庫操做使用C++對sqlit3庫進行封裝並結合Qjson實現ORM自動操做。Sqlit3的動態連接庫,在windows、linux、mac各平臺下須要從新編譯。linux

效果圖

關鍵代碼分析

cmake之操做系統選擇

if(MSVC)                            #windows
        set(EnclibName Sqlit3.dll)
elseif(APPLE)                        #mac
        set(EnclibName sqlite3.o)
elseif(UNIX)                            #linux
        set(EnclibName libsqlite3.so)
endif()

Qjson.h - json操做封裝

個人數據操做實現中的信息傳遞,都是以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對象,類內部使用
};

數據庫通用接口 - Idb.h

經實踐總結,數據庫操做有如下接口,百分之九十以上的需求就都能知足。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;
};

數據庫操做標準實現 - DbBase.h

咱們的上層應用都是操做這個實現,這個類組合了一個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的C++封裝類 - Sqlit3Db.h

實現了Sqlit3的Idb接口,下面抽了幾個關鍵點進行一些說明:sql

std::unique_ptr

數據鏈接使用了std::unique_ptr,在gcc下必定要引入 memory 頭文件,這個問題折騰了我很久,在windows下沒出問題,但在linux下一直報錯。數據庫

sql語句中中文的支持

要保證送入底層的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);
        }

JOSN-ORM的使用方法

數據查詢使用的智能的ORM實現,具體方法請參照前文《c++關係數據庫訪問通用接口設計》segmentfault

Playback 功能實現

回放設置了幾個參數

  • last : 最後一次遊戲的回放
  • one :積分排行第一的遊戲
  • two :積分排行第二的遊戲
  • three :積分排行第三的遊戲

源代碼及運行方法

項目採用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
相關文章
相關標籤/搜索