QT之贪吃蛇

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT之贪吃蛇相关的知识,希望对你有一定的参考价值。

1、开始界面

技术分享

 

对话框设置:设置对话框控件以及标题

 

  1. GameStart::GameStart(QDialog *parent)
  2.     : QDialog(parent)
  3. {
  4.  
  5.     createWidgets();
  6.     setWindowTitle(tr("Snake"));
  7. }

 

设置3个按钮,连接三种操作:开始游戏,获得历史排名,结束游戏。

 

  1.     beginGame=new QPushButton(tr("start"));
  2.     beginGame->setFixedSize(120, 30);
  3.     beginGame->setFont(font);
  4.     connect(beginGame, SIGNAL(clicked()), this, SLOT(startGame()));
  5.  
  6.     endGame=new QPushButton(tr("end"));
  7.     endGame->setFixedSize(120, 30);
  8.     endGame->setFont(font);
  9.     connect(endGame, SIGNAL(clicked()), this, SLOT(close()));
  10.  
  11.     GameRank=new QPushButton(tr("rank"));
  12.     GameRank->setFixedSize(120, 30);
  13.     GameRank->setFont(font);
  14.     connect(GameRank, SIGNAL(clicked()), this, SLOT(loadRank()));

 

设置游戏等级选项,含有3个等级。

 

  1.     GameLevelCombo=new QComboBox(this);
  2.     GameLevelCombo->setFixedSize(120, 30);
  3.     GameLevelCombo->setFont(font);
  4.     GameLevelCombo->addItem(tr("elementary"));
  5.     GameLevelCombo->addItem(tr("intermediate"));
  6.     GameLevelCombo->addItem(tr("advanced"));
  7.     connect(GameLevelCombo, SIGNAL(activated(const QString &)), this, SLOT(getGameLevel()));

 

设置label和lineedit,用于输入玩家姓名。

 

  1.     NameLabel=new QLabel(tr("player"));
  2.     NameLabel->setFixedSize(120, 30);
  3.     NameLabel->setFont(font);
  4.     NameEdit=new QLineEdit;
  5.     NameEdit->setFixedSize(200, 30);
  6.     connect(NameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(setName()));

 

利用QTextEdit来显示排名信息。

 

  1.     RankText=new QTextEdit(this);
  2.     RankText->setStyleSheet("background:argb(248, 248, 255, 0%)");
  3.     RankLabel=new QLabel(tr("show rank"));
  4.     RankLabel->setFixedSize(100, 30);
  5.     RankLabel->setFont(font);

 

布局:

 

  1.     QVBoxLayout *LeftLayout=new QVBoxLayout;
  2.     LeftLayout->addWidget(beginGame);
  3.     LeftLayout->addWidget(GameRank);
  4.     LeftLayout->addWidget(GameLevelCombo);
  5.     LeftLayout->addWidget(endGame);
  6.  
  7.     QVBoxLayout *RightLayout=new QVBoxLayout;
  8.     RightLayout->addWidget(RankLabel);
  9.     RightLayout->addWidget(RankText);
  10.  
  11.     QHBoxLayout *TopLayout=new QHBoxLayout;
  12.     TopLayout->addWidget(NameLabel);
  13.     TopLayout->addWidget(NameEdit);
  14.  
  15.  
  16.     QHBoxLayout *BelowLayout=new QHBoxLayout;
  17.     BelowLayout->addLayout(LeftLayout);
  18.     BelowLayout->addLayout(RightLayout);
  19.  
  20.     QVBoxLayout *MainLayout=new QVBoxLayout;
  21.     MainLayout->addLayout(TopLayout);
  22.     MainLayout->addLayout(BelowLayout);
  23.     setLayout(MainLayout);

 

玩家名字获得、获得排名信息、产生游戏等级的函数实现:

 

  1. void GameStart::setName(){
  2.     player=NameEdit->text();
  3.  
  4. }
  5.  
  6.  
  7. void GameStart::getGameLevel(){
  8.     QString LevelString=GameLevelCombo->currentText();
  9.     if(LevelString==tr("elementary"))
  10.         GameLevel=1;
  11.     else if(LevelString==tr("intermediate"))
  12.         GameLevel=2;
  13.     else if(LevelString==tr("advanced"))
  14.         GameLevel=3;
  15.     else
  16.         GameLevel=1;
  17. }
  18.  
  19.  
  20.  
  21.  
  22. void GameStart::loadRank(){
  23.     QFile file("H:/soft programme/QTMySnaker/snake/Rank.txt");
  24.     if(!file.open(QFile::ReadOnly)){
  25.         QMessageBox::warning(this, tr("load rank"), tr("cannot load rank file %1").arg(file.fileName()));
  26.         return;
  27.     }
  28.      QTextStream in(&file);
  29.      RankText->setPlainText(in.readAll());
  30.  
  31. }

 

对于游戏界面调出,则要求能够隐藏当前对话框,并且终止当前线程,而可以去执行游戏界面的线程,那么需要在游戏界面重新实现一个执行函数exec()。

 

  1. void GameStart::startGame(){
  2.  
  3.     this->close();
  4.  
  5.     ImageShow ImageShow_M1(player,GameLevel);
  6.     ImageShow_M1.exec();
  7.  
  8.     this->show();
  9.     this->exec();
  10.  
  11.  
  12. }

 

如果仅仅是游戏界面用show()函数,则游戏界面不会产生控件,仅仅会出现一个主窗口。这就是模式和非模式显示,模式不仅显示界面而且会锁定在这个界面上,从而执行操作。一般QDialog有自己的exec()函数,而QWidget没有,这就需要在QWidget中实现一个。

 

2、游戏界面

技术分享

 

初始化、定义计时器:

  1. ImageShow::ImageShow(const QString player, const int game_level):
  2.     player(player), game_level(game_level){
  3.  
  4.  
  5.     setWindowTitle(tr("In Game..."));
  6.     setFixedSize(ROWS+20, COLUMNS+50);
  7.     InitSnake();
  8.  
  9.     Timer=new QTimer(this);
  10.     connect(Timer, SIGNAL(timeout()),this, SLOT(timeout()));
  11.     Timer->start(1000/game_level);
  12.  
  13. }

 

  1. void ImageShow::InitSnake(){
  2.  
  3.     game_score = 0;
  4.     IsOver=false;
  5.     direction=3;//right
  6.  
  7.     body_x = new vector<char>;
  8.     body_y = new vector<char>;
  9.  
  10.     for (int i = 4; i >= 1; i--) {
  11.         body_x->push_back(1);
  12.         body_y->push_back(i);//初始化具有1个蛇头和3个蛇身的蛇
  13.  
  14.     }
  15.     food_x = 3;
  16.     food_y = 5;
  17.  
  18.  
  19. }

 

  1. void ImageShow::timeout(){
  2.  
  3.      IsOver=walk();
  4.      if(IsOver == true){
  5.          Timer->stop();
  6.  
  7.  
  8.          int r=QMessageBox::warning(this,tr("save rank"), tr("geme is over!\\n"
  9.                                                              "Do you want to save the game results?"),
  10.                                      QMessageBox::Yes|QMessageBox::No);
  11.           if(r==QMessageBox::Yes)
  12.             saveRank();
  13.          return;
  14.      }
  15.  
  16.      update();
  17.  
  18.  
  19. }

根据游戏等级,产生了不同时间限定,时间到就会去执行蛇行走,然后再绘制图像,通过调用了update函数实现绘制事件。

 

  1. bool ImageShow::walk() {
  2.  
  3.  
  4.  
  5.     switch (direction) {
  6.     case 1: {
  7.         body_x->insert(body_x->begin(), (*body_x)[0]);
  8.         body_x->pop_back();
  9.         body_y->insert(body_y->begin(), (*body_y)[0] - 1);
  10.         body_y->pop_back();
  11.         break;
  12.     }//左移动
  13.     case 2: {
  14.         body_x->insert(body_x->begin(), (*body_x)[0] - 1);
  15.         body_x->pop_back();
  16.         body_y->insert(body_y->begin(), (*body_y)[0]);
  17.         body_y->pop_back();
  18.         break;
  19.     }//向上移动
  20.     case 3: {
  21.         body_x->insert(body_x->begin(), (*body_x)[0]);
  22.         body_x->pop_back();
  23.         body_y->insert(body_y->begin(), (*body_y)[0] + 1);
  24.         body_y->pop_back();
  25.         break;
  26.     }//向右移动
  27.     case 4: {
  28.         body_x->insert(body_x->begin(), (*body_x)[0] + 1);
  29.         body_x->pop_back();
  30.         body_y->insert(body_y->begin(), (*body_y)[0]);
  31.         body_y->pop_back();
  32.         break;
  33.     }//向下移动
  34.  
  35.     default: ;
  36.  
  37.  
  38.     }
  39.  
  40.  
  41.     if (((*body_x)[0] == food_x) && ((*body_y)[0] == food_y)) {
  42.         body_x->push_back(body_x->back());
  43.         body_y->push_back(body_y->back());
  44.         game_score++;
  45.         produce();
  46.     }//吃下食物
  47.  
  48.     if (((*body_x)[0] == 0) || ((*body_x)[0] == (ROWS/8)) || ((*body_y)[0] == 0) || ((*body_y)[0] == (COLUMNS/8))) {
  49.  
  50.  
  51.         return true;//蛇撞墙,游戏失败
  52.  
  53.     }
  54.     return false;
  55.  
  56.  
  57.  
  58.  
  59. }

 

  1. void ImageShow::paintEvent(QPaintEvent *){
  2.  
  3.     QPainter *painter = new QPainter(this);
  4.     painter->setRenderHint(QPainter::Antialiasing, true);
  5.  
  6.     QPen ThinPen(palette().foreground(), 1);
  7.  
  8.     QColor LightSlateBlue(132, 112, 255);
  9.     QPen ThickPen(LightSlateBlue, 4);
  10.  
  11.     painter->save();
  12.     painter->translate(10, 40);
  13.     painter->setPen(ThickPen);
  14.     painter->setBrush(LightSlateBlue);
  15.     painter->drawLine(0, 0, ROWS, 0);
  16.     painter->drawLine(0, 0, 0, COLUMNS);
  17.     painter->drawLine(ROWS, 0, ROWS, COLUMNS);
  18.     painter->drawLine(0, COLUMNS, ROWS, COLUMNS);
  19.  
  20.     painter->restore();
  21.  
  22.  
  23.     QColor brown(255,64,64);
  24.     QPen textPen(brown, 4);
  25.     painter->setPen(textPen);
  26.     QFont font=painter->font();
  27.     font.setPixelSize(20);
  28.     painter->setFont(font);
  29.     const QRect rectangle1=QRect(50, 2, 150, 25);
  30.     QRect boundingrect;
  31.     painter->drawText(rectangle1, 0, player, &boundingrect);
  32.     const QRect rectangle2=QRect(130, 2, 150, 25);
  33.     painter->drawText(rectangle2, 0, tr("Game Level: %1").arg(game_level), &boundingrect);
  34.     const QRect rectangle3=QRect(300, 2, 150, 25);
  35.     painter->drawText(rectangle3, 0, tr("game score: %1").arg(game_score), &boundingrect);
  36.  
  37.     painter->save();
  38.     painter->rotate(270);
  39.     painter->translate(-40, 10);
  40.  
  41.     QPoint FoodPoint(-food_x*8,food_y*8);
  42.  
  43.     QRadialGradient RadialGradient(-food_x*8, food_y*8, 6, -food_x*8, food_y*8);
  44.     RadialGradient.setColorAt(0, Qt::green);
  45.     RadialGradient.setColorAt(1.0, Qt::darkGreen);
  46.     painter->setPen(Qt::NoPen);
  47.     painter->setBrush(RadialGradient);
  48.     painter->drawEllipse(FoodPoint, 6, 6);
  49.  
  50.     QPoint SnakeHeadPoint(-8*((*body_x)[0]), 8*((*body_y)[0]));
  51.     QConicalGradient ConicalGradient(-8*((*body_x)[0]), 8*((*body_y)[0]), 270);
  52.     ConicalGradient.setColorAt(0,Qt::red);
  53.     ConicalGradient.setColorAt(1,Qt::darkRed);
  54.     painter->setPen(ThinPen);
  55.     painter->setBrush(ConicalGradient);
  56.     painter->drawEllipse(SnakeHeadPoint, 5, 5);
  57.  
  58.     QColor LightSalmon(255,160,122);
  59.     painter->setPen(ThinPen);
  60.     painter->setBrush(LightSalmon);
  61.  
  62.     for(int i=1;i<body_x->size();i++){
  63.         painter->drawRect(-8*((*body_x)[i])-4, 8*((*body_y)[i])-4,8,8);
  64.  
  65.     }
  66.  
  67.     painter->restore();
  68.  
  69.  
  70.  
  71.  
  72. }

 

对于蛇的移动和控制,通过重载keyPressEvent函数来实现。

 

  1. void ImageShow::keyPressEvent(QKeyEvent *KeyEvent){
  2.  
  3.     switch(KeyEvent->key()){
  4.     case Qt::Key_Left: {direction = 1;break;}
  5.     case Qt::Key_Up:{direction=2;break;}
  6.     case Qt::Key_Right:{direction=3;break;}
  7.     case Qt::Key_Down:{direction=4;break;}
  8.     default:break;
  9.     }
  10.  
  11.  
  12.      QWidget::keyPressEvent(KeyEvent);
  13.  
  14. }

 

保存游戏结果,并且将原来游戏历史记录根据得分高低排序。

 

  1. void ImageShow::saveRank(){
  2.  
  3.     archive archive_now;
  4.     archive archive_history[10];
  5.     int archive_num=0;
  6.  
  7.     string PlayerString=player.toStdString();
  8.     strcpy(archive_now.player_temp, PlayerString.c_str());
  9.     archive_now.game_leve_temp=game_level;
  10.     archive_now.game_score_temp=game_score;
  11.  
  12.     QFile File("H:/soft programme/QTMySnaker/snake/Rank.txt");
  13.     if(!File.open(QIODevice::ReadWrite))
  14.         return;
  15.     QTextStream InOut(&File);
  16.     while(!File.atEnd()){
  17.         QString player_string;
  18.         InOut>>player_string>>archive_history[archive_num].game_leve_temp
  19.                 >>archive_history[archive_num].game_score_temp;
  20.         string PlayerString=player_string.toStdString();
  21.         strcpy(archive_history[archive_num].player_temp, PlayerString.c_str());
  22.         archive_num++;
  23.     }
  24.  
  25.  
  26.  
  27.  
  28.     for(int i=0;i<=archive_num;i++){
  29.  
  30.         if(archive_now.game_score_temp>archive_history[i].game_score_temp){
  31.           for(int j=archive_num;j>=i+1;j--){
  32.  
  33.               archive_history[j]=archive_history[j-1];
  34.           }
  35.             archive_history[i]=archive_now;
  36.             break;
  37.         }
  38.         else if(i==archive_num){
  39.             archive_history[archive_num]=archive_now;
  40.         }
  41.  
  42.     }
  43.  
  44.     for(int i=0;i<=archive_num;i++){
  45.         InOut<<archive_history[i].player_temp<<‘\\t‘
  46.             <<archive_history[i].game_leve_temp<<‘\\t‘
  47.            <<archive_history[i].game_score_temp<<‘\\n‘;
  48.     }
  49.     File.close();
  50.  
  51.  
  52. }

 

接下来就是如何让这个界面能够在开始界面点击开始后可以执行,通过利用一个QEventLoop,可以将执行锁定住,然后为了退出循环,则可以通过关闭事件来调用其退出。

 

  1. void ImageShow::exec(){
  2.  
  3.     this->show();
  4.     QEventLoop loop;
  5.     m_loop=&loop;
  6.     loop.exec();
  7.  
  8.  
  9. }
  10.  
  11. void ImageShow::closeEvent(QCloseEvent *event){
  12.     this->hide();
  13.     m_loop->exit();
  14.     event->accept();
  15.  
  16. }

 

以上是关于QT之贪吃蛇的主要内容,如果未能解决你的问题,请参考以下文章

Qt实现贪吃蛇简单示例

QT下的贪吃蛇

QT实现两条贪吃蛇

经典游戏还原之:贪吃蛇

C语言之贪吃蛇(ncurses)

使用VB创建贪吃蛇