天天看點

基于QT的翻金币小遊戲項目總結記錄(一)

前言

  最近為了學習C++和qt5,跟着教程寫了一個翻金币的小遊戲,源碼和資源檔案的連結在這裡:翻金币小遊戲源碼和資源檔案的下載下傳連結。裡面有自己寫的超多代碼注釋,隻要2個C币,去秒。

  作為第一個QT項目,還是有必要做些總結和一些細節方面的記錄的。

遊戲的組成

  我認為遊戲的組成主要是兩個部分:遊戲資源和遊戲機制。

  遊戲資源泛指遊戲中給使用者提供的視聽資源,比如遊戲的場景,各種道具和角色的外觀,遊戲音樂,與使用者進行互動的界面等。

  遊戲機制泛指遊戲中實作遊戲運作的方式,在翻金币小遊戲中,怎麼排列錢币,如何翻轉錢币,如何判斷遊戲的勝利與失敗,這種對于使用者來講相對隐式的存在卻在遊戲開發中占據核心地位。遊戲機制的實作通常需要資料結構與相關算法的結合。

視窗

視窗類型的選擇

  在初始化各種視窗的時候,是選擇QMainWindow,Qwidget,還是QDialog類型呢?這個主要看該視窗的功能,如果作為主視窗(比如互動視窗,遊戲視窗)存在的話,選擇QMainWindow類型最合适,因為QMainWindow可以搭載工具欄和狀态欄,這些功能選擇元件在小遊戲裡面是很實用的(尤其是工具欄)。

  至于QDialog則用于對話視窗,單方向地由軟體向使用者發送資訊,一般不做互動,遊戲的遊戲說明,提示,警告都适合使用QDialog類型。

  QWidget适合用來自定義元件,這次的項目中沒有組合類的自定義元件,是以沒有使用。該項目中自定義的是單個類型的元件(新的按鈕),是以直接去繼承QT裡面的button類就可以了。

不同視窗類型代碼的切換

  在qt裡,自己的視窗是啥類型全靠繼承決定。

#include <QMainWindow>
class MainScene : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainScene(QWidget *parent = 0);
    ~MainScene();
private:
    Ui::MainScene *ui;
};
           

  以上是一個QMainWindow類型的視窗,為什麼它是QMainWindow類型的?

  1.它引用的頭檔案是QMainWindow

  2.這個類繼承自QMainWindow

  3.這個類的構造函數使用了父類QWidget的對象指針,在構造函數實作時寫為父類QMainWindow的對象指針

  如果我們想将其修改為QDialog類型呢?

#include <QDialog>
class MainScene : public QDialog
{
    Q_OBJECT

public:
    explicit MainScene(QWidget *parent = 0);
    ~MainScene();
private:
    Ui::MainScene *ui;
};
           

  可見,隻需要修改其繼承關系,就可以修改視窗的類型了。

  這裡切忌去死記硬背,去背該去修改這裡還是修改那裡,像繼承這種技術屬于C++裡面的核心機制,應當是完全依靠了解去修改的。

遊戲運作時視窗之間的切換

  遊戲運作時視窗之間切換似乎挺簡單,讓目前的視窗hide一下,讓下一個視窗show一下,好像就可以了,但是在實際的項目中要比這複雜。

  普通視窗之間的切換:

  假設有兩個視窗,視窗A和視窗B,我們希望實作:初始為視窗A,可以切換到視窗B,從視窗B還可以傳回到視窗A 。

  從視窗A切換到視窗B比較簡單,首先我們在代碼中實作兩個類,類A的執行個體化對象就是視窗A,類B的執行個體化對象就是視窗B,在類A中設定一個類B的對象指針。在函數中,執行個體化該對象指針(建立個對象,即視窗B),并在觸發函數中令目前A對象hide一下,讓B的對象指針show一下即可。

基于QT的翻金币小遊戲項目總結記錄(一)

  

  上圖所示,遊戲主界面和關卡選擇界面就是這樣切換的,當然在此之前需要執行個體化關卡選擇界面對象。

  如何實作視窗B到視窗A的傳回呢?

  其實傳回操作也是主要在A的函數裡面實作的,我們可以在A的函數裡寫一個觸發函數,該觸發函數實作視窗B的hide與視窗A的show。

  其實說到這裡也很清晰了,在B類裡面定義一個信号函數,隻需要聲明不需要實作,當需要傳回視窗A的時候發出該信号就好了,A類裡面的觸發函數等着接受,一旦收到了就執行視窗B到A的切換。

基于QT的翻金币小遊戲項目總結記錄(一)

  

  如下圖所示,在B中聲明一個信号函數(無需實作),在需要的時候使用關鍵字emit将其發送出去即可。

  

基于QT的翻金币小遊戲項目總結記錄(一)

  

  普通視窗與遊戲遊玩視窗之間的切換

  這裡我們模拟一下關卡選擇視窗和遊戲遊玩視窗之間切換的過程:首先我們在關卡選擇視窗選擇第一關,進入第一關的遊戲遊玩視窗,發現第一關太簡單,就直接傳回關卡選擇界面去選下一關……如此下來,如果我們一關都沒通關,而且頻繁的進行關卡選擇與傳回操作之後,按照上面的操作方法,會建立出來非常多的遊戲遊玩視窗的對象,雖然這些對象基本都都hide起來了,但是依舊是存在于記憶體中吃資源的。

  當然,每打開一個新視窗都需要執行個體化一個新對象,關鍵是,這些對象在目前沒有投入使用的時候需要一直存在并且hide嗎?

  當思考到這裡的時候答案就已經呼之欲出了:遊戲遊玩視窗在關閉或者傳回的時候應該将對應的對象删除掉。

  

基于QT的翻金币小遊戲項目總結記錄(一)

  

  按照上圖所示,在傳回關卡選擇視窗的時候應該将遊戲遊玩視窗對象删除掉。

  這裡注意一下C++是如何删除掉一個對象指針的:首先

delete +對象指針名

将對象指針指向的對象删除掉,再

對象指針名=nullptr

将該指針置空,令其不再指向任何記憶體,達成回收指針的目的。如果沒有第二步的話,該指針就會成為一個野指針,依舊指向一塊記憶體,如果該記憶體後續加入了其他内容,這指針就等于指向了新的内容,依舊能被使用,這是不能容忍的。一個指針隻有置空才算完成回收。

對象的建立方式

  在c++對象初始化一般為兩種:一種是直接初始化對象,該對象存在于棧中;一種是使用對象指針指向對象對象,該對象存在于堆中。

  個人認為使用對象指針的優點是:可以在有需要的時候再建立對象,沒有需要的時候指針是初始化置空的,擱一邊就行,比較靈活。而直接初始化對象,不管你接下來用不用,這個對象一定是存在着,占你資源的,是以相對不夠靈活,優點是省事,不用再去指派一次(對象指針用的時候還得new一次指向對象)。

  

基于QT的翻金币小遊戲項目總結記錄(一)

  

  在工程中我都是使用對象指針的方式管理對象成員,一定記得要執行個體化操作。

  

模态對話框的建立方式

  

基于QT的翻金币小遊戲項目總結記錄(一)

  

  建立對話框一定注意對話框的對象回收機制,由于QT存在對象樹機制,當主界面關閉的時候才會将所有的對象回收,這裡就衍生出了一個問題,如果主界面沒有關閉,通過一些鬼畜的重複操作是不是可以建立相當數量的對象而不回收,進而導緻記憶體洩漏?

  答案是肯定的,這也是為什麼上文中遊戲視窗需要手動回收對象,對話框同樣存在該種隐患,是以這裡需要加入一個對象回收機制

setAttribute

函數來保證每次關閉對話框都會回收該對象。

  但是這樣也引出了另一個問題:為了保證跨平台使用,QT的

setAttribute

函數隻删除對象,沒有對對象指針置空,形成了野指針,野指針再次使用會導緻程式異常結束。

  是以對話框對象指針每次使用都需要指向建立對象,以避免使用了野指針。

  

  PS:項目細節确實多,做項目賊練程式設計能力。

  程式設計項目做就完事了奧力給。

繼續閱讀