《C++ Qt 设计模式》8|15拼图 小游戏的简单实现.拜托,别乱点!

发布于 2022-06-02 06:28

第零章:介绍
  看到这个游戏了,感觉蛮好玩的,实现了一下。
  界面如下:

第一章:构思
  设计模式基本上没接触过,所以就没有按书上的方式,自己想了大概要怎么实现,可能自己像的没有它给出的方式好吧,但是毕竟是菜鸟嘛,一步一步来!
1、用什么装这些按钮
  学习了QGridLayOut,“The QGridLayout class lays out widgets in a grid”,这就好办了,它会把窗体控件放到一个网格里面,也就是说类似与矩阵啦,ABC……这些肯定就是就是一个个QPushButton啦,创建了一个个按钮,再把它装进去即可。最后这个QGridLayOut设置为QDialog的LayOut就可以了。
  这个是从显示层面考虑的。
2、如何用代码表示一个3×3的矩阵
  虽然可以把一个窗体放到一个QGridLayOut来进行布局,它有如下添加函数:
void    addWidget ( QWidget * widget, int row, int column, Qt::Alignment alignment = 0 )
但是由于我对QGridLayOut不熟,不知道是否可以用类似与矩阵的存取方式,即给定行列,获取里面的东西。所以我用了一个矩阵,也就是一个二维数组啦,对应每一个按钮,数组里面放的是指向按钮的指针,空白就放一个NULL指针,因为几个按钮创建好了就在内存那里了,不动了,所以以后我交换一个按钮与一个空白的时候就这要交换这两个指针就加上重新QGridLayOut的方法addWidget就可以了。
3、需要哪些类
  需要两个类,一个MyButton,公有继承QPushButton,还有Dialog类,公有继承QDialog,即显示主界面啦。
第三章:MyButton类的实现
  直接上代码:
#ifndef MYBUTTON_H #define MYBUTTON_H  #include <QPushButton>   struct Coord {     int x;     int y; };   class MyButton : public QPushButton {     Q_OBJECT  private:     Coord m_coord;  public:     explicit MyButton(char c,Coord coord,QWidget* parent=0);     void setCoord(Coord newCd);     Coord getCoord() const;  signals:     void myClick(MyButton* p);  //signal,argument is poiter to myself,so someone else can identify me  private slots:     void btClicked(); };  #endif // MYBUTTON_Hmybutton.h
#include "mybutton.h"  #include <QMessageBox>  MyButton::MyButton(char c,Coord coord, QWidget *parent) : QPushButton(parent) {      setText(QString("%1").arg(c));     this->m_coord=coord;     connect(this,SIGNAL(clicked()),this,SLOT(btClicked())); }  void MyButton::setCoord(Coord newCd) {     m_coord=newCd; }  Coord MyButton::getCoord() const {     return m_coord; }   void MyButton::btClicked() {     emit myClick(this); }
mybutton.cpp
我给每一个按钮一个坐标的数据了,因为按钮创建好了就在内存的某个位置不动了,如何表示他们对应显示的哪一个呢?这个坐标就相当于标示一个按钮,ABCD……只是他们显示的文字,这个是不变的。
  构造函数的参数char是它要显示的东西,Coord是自定义结构体表示坐标。
 connect(this,SIGNAL(clicked()),this,SLOT(btClicked()));void MyButton::btClicked(){    emit myClick(this);}
注意这个,这样就可以让Dialog类自己写一个槽,这个槽由上面的this参数就可以知道是哪个按钮被点了,在这里居然想了好久……

第四章:Dialog类的实现
1、类的声明:
#ifndef DIALOG_H #define DIALOG_H  #include <QDialog> #include "mybutton.h"  class QGridLayout; class QPushButton;  namespace Ui { class Dialog; }  class Dialog : public QDialog {     Q_OBJECT  public:     explicit Dialog(QWidget *parent = 0);     ~Dialog();  private:     Ui::Dialog *ui;      static const int N=3;     QGridLayout* m_lay;     MyButton* m_pbArr[N][N];     QString m_btnTextOrder; private slots:     void btClicked(MyButton* p);   //one of N*N buttons has been clicked  private:     void disorder();    //disorder the N*N buttons     void exchangeButton(Coord a,Coord b);    //exchange two buttons based on coord     bool isOver() const; //is it in order??? };  #endif // DIALOG_H
dialog.h

  一一说明数据成员的含义,源代码没有注释是因为输入不了中文,我才不要鸡鸡比注释短呢……
  static const int N=3;  表明是几乘几的矩阵
  QGridLayout* m_lay;  布局
  MyButton* m_pbArr[N][N];  对应布局的矩阵,数组里面放的都是指向按钮的指针,移动一个按钮只要交换对应的指针并把按钮的私有坐标改一下并在布局里面弄一下即可
  QString m_btnTextOrder;  用到就知道了
2、构造函数:
Dialog::Dialog(QWidget *parent) :     QDialog(parent),     ui(new Ui::Dialog) {     ui->setupUi(this);     setWindowTitle("Let us have a game!");     setMinimumSize(300,250);     setMaximumSize(300,250);      m_lay=new QGridLayout(this);      for(int i=0;i<N;i++)     {         int colums;         if(i<N-1) colums=N;         else colums=N-1;          for(int j=0;j<colums;j++)         {             char c=i*N+j+'A';             m_btnTextOrder.append(c);              Coord cd={i,j};             m_pbArr[i][j]=new MyButton(c,cd);              m_lay->addWidget(m_pbArr[i][j],i,j);             connect(m_pbArr[i][j],SIGNAL(myClick(MyButton*)),this,SLOT(btClicked(MyButton*)));         }     }     m_pbArr[N-1][N-1]=NULL;     this->setLayout(m_lay);      disorder(); //make buttons disorder }
构造函数

  先把窗口大小固定了。
  new一个QGridLayout。
  最后把最后一个位置的没有按钮的赋值为空指针。
  最最后使这些按钮无序,这个稍后介绍。
void Dialog::btClicked(MyButton* p) {     Coord cd=p->getCoord(); //get the button that been clicked     Coord cdTarget=cd; //target position that maybe switch      if((cd.x-1>=0)&&(m_pbArr[cd.x-1][cd.y]==NULL))  //test top     {         cdTarget.x--;     }     else if((cd.x+1<N)&&(m_pbArr[cd.x+1][cd.y]==NULL))  //test down     {         cdTarget.x++;     }     else if((cd.y-1>=0)&&(m_pbArr[cd.x][cd.y-1]==NULL))   //test left     {         cdTarget.y--;     }     else if((cd.y+1<N)&&(m_pbArr[cd.x][cd.y+1]==NULL))  //test right     {         cdTarget.y++;     }     else     {         return; //can not move this button     }      /*let us switch!*/     exchangeButton(cd,cdTarget);      /*check whether game is over*/     if(isOver())     {         QMessageBox::warning(this,"Sucess","You made it! Congratulations!");     } }

void Dialog::exchangeButton(Coord a, Coord b) {     if((m_pbArr[a.x][a.y]!=NULL)&&(m_pbArr[b.x][b.y]!=NULL))    //no NULL     {         /*remove from pre QGridLayOut*/         m_lay->removeWidget(m_pbArr[a.x][a.y]);         m_lay->removeWidget(m_pbArr[b.x][b.y]);          /*add to QGridLayOut*/         m_lay->addWidget(m_pbArr[a.x][a.y],b.x,b.y);         m_lay->addWidget(m_pbArr[b.x][b.y],a.x,a.y);          /*change two buttons's coord */         m_pbArr[a.x][a.y]->setCoord(b);         m_pbArr[b.x][b.y]->setCoord(a);          /*exchange poiter in m_pbArr[] */         MyButton* temp=m_pbArr[a.x][a.y];         m_pbArr[a.x][a.y]=m_pbArr[b.x][b.y];         m_pbArr[b.x][b.y]=temp;     }     else    //one of two buttons' position is NULL (in array m_pbArr[])     {         if(m_pbArr[a.x][a.y]==NULL)         {             Coord temp=a;             a=b;             b=temp;         }          /*here,we can make sure that coord b is null*/         m_lay->removeWidget(m_pbArr[a.x][a.y]);         m_lay->addWidget(m_pbArr[a.x][a.y],b.x,b.y);         m_pbArr[a.x][a.y]->setCoord(b);         MyButton* temp=m_pbArr[a.x][a.y];         m_pbArr[a.x][a.y]=m_pbArr[b.x][b.y];         m_pbArr[b.x][b.y]=temp;     } }
交换按钮

交换分为两周情况,两个按钮的交换,这个再使界面无序化时调用,另一个就是交换按钮与空白,只要看前一个就足够了。
  先把待交换的两个按钮从QGridLayOut拿下来,具体表现就是不显示了。
  再交叉添加到QGridLayOut的特定坐标。
  再把两个按钮的私有数据也就是坐标交换一下,因为坐标通过这个坐标从得到被点按钮的位置的。
  再把数据层(QGridLayOut是显示层)的指针也互换一下,让他们指向对的按钮内存。
4、使无序化
void Dialog::disorder() {     Coord cdA,cdB;     for(int i=0;i<56;i++)     {         QTime time=QTime::currentTime();         qsrand(time.msec()*7+time.second()*245);         cdA.x=qrand()%N; //from 0 to N-1         qsrand(time.second()+i*3+91);         cdA.y=qrand()%N;          qsrand(time.msec()*3+i);         cdB.x=qrand()%N;         qsrand(time.msec()+i*11);         cdB.y=qrand()%N;      //    qDebug()<<cdA.x<<cdA.y<<"-----"<<cdB.x<<cdB.y<<endl;         if((cdA.x==cdB.x)&&(cdA.y==cdB.y))         {             i--;         }         else    //not the same coord         {             exchangeButton(cdA,cdB);         }     } }
无序化

  游戏开始是无序的,这个函数就是干这事的,有了上面的交换,那么只要随即生成坐标,把指定坐标的按钮交换即可。关键这里的产生随即坐标的函数,我写的不太好,调试时可以看出好多对坐标一样,因为计算机太快了。
5、检测是否结束
bool Dialog::isOver() const {     QString res;     for(int i=0;i<N;i++)     {         for(int j=0;j<N;j++)         {             if(m_pbArr[i][j]!=NULL)             {                 res.append(m_pbArr[i][j]->text());             }         }     }      if(res==m_btnTextOrder)     {         return true;     }     else     {         return false;     } }
是否结束

第五章:遗言
1、QGridLayOut
  显示与数据表示分离了,这个不太好。
2、啦啦啦啦啦啦
  我会告诉你我只有一次成功了吗?
  我想随机交换之后会不会本身就有成功不了的可能?数学理论啊!

本文来自网络或网友投稿,如有侵犯您的权益,请发邮件至:aisoutu@outlook.com 我们将第一时间删除。

相关素材