QT实现简单贪吃蛇-第一次做

widget.h中的代码

 1 #ifndef WIDGET_H
 2 #define WIDGET_H
 3 #include <QWidget>
 4 #include <QKeyEvent>
 5 #include <QRectF>
 6 #include <QPainter>
 7 #include <QPen>
 8 #include <QBrush>
 9 #include <QDebug>
10 #include <QTimer>
11 #include <QTime>
12 #include <QDebug>
13 #include <QTime>
14 #include <QPaintEvent>
15 #include <QTimerEvent>
16 #include <QIcon>
17 #include <QPushButton>
18 #include <QVBoxLayout>
19 #include <QHBoxLayout>
20 #include <QPalette>
21 class Widget : public QWidget
22 {
23     Q_OBJECT
24 public:
25     Widget(QWidget *parent = nullptr);
26     ~Widget();
27     //char moveFlag = 'U';//定义初始方向
28     void deleteAllitemsOfLayout(QLayout* layout);//删除布局
29 protected:
30     void paintEvent(QPaintEvent *event);
31     void timerEvent(QTimerEvent *event);
32     void keyPressEvent(QKeyEvent *event);
33 private:
34     void moveLeft();//向左移动
35     void moveRight();//向右移动
36     void moveUp();//向上移动
37     void moveDown();//向下移动
38     void deleteLastRectF();//删除最后一个节点
39     void iseatFood();//吃到了食物
40     bool isGameover();//是否结束游戏
41 protected slots:
42     void food();//生成食物
43     void pb_Start();//开始游戏
44     void pb_Quit();//退出游戏
45 private:
46     int snake_long = 4;//初始长度
47     QList<int> snake_x;//定义一个int类型的数据  x轴
48     QList<int> snake_y;//定义一个int类型的数据  y轴
49     int snake_size = 10;//定义单个身体大小
50     char moveFlag = 'U';//定义初始方向
51     QList<int> food_x;//食物
52     QList<int> food_y;//食物
53     int food_t = 0;//设置生成食物的时间
54     int timestop = 0;
55     int timerid;
56     //QTimer *foodTimer;
57     int gameover = 0;
58 private:
59     QPushButton *pb_start, *pb_quit;
60 };
61 #endif // WIDGET_H
游戏主要通过绘图事件来实现,首先实现“开始游戏”和“退出游戏”,利用QPushBottom生成两个按钮,并且点击会有相应的操作
 1 Widget::Widget(QWidget *parent)
 2     : QWidget(parent)
 3 {
 4     this->setWindowTitle("贪吃蛇");
 5     this->setWindowIcon(QIcon(":/pic/1.png"));
 6     resize(500, 500);//设置窗口大小
 7     setStyleSheet("QWidget{background:white}");
 8     for(int i = 0; i < snake_long; i++)
 9     {
10         snake_x.append(250);
11         snake_y.append(400 + i * 10);
12     }
13     food();//生成一个食物
14     QFont font("方正舒体",20,QFont::ExtraLight, false);
15     pb_start = new QPushButton;
16     pb_start->setText("开始游戏");
17     pb_start->setFixedSize(200, 80);
18     pb_start->setFont(font);
19     pb_start->setFlat(true);
20     pb_quit = new QPushButton;
21     pb_quit->setText("退出游戏");
22     pb_quit->setFixedSize(200, 80);
23     pb_quit->setFont(font);
24     pb_quit->setFlat(true);
25     QHBoxLayout *hbox1 = new QHBoxLayout;
26     hbox1->addStretch();
27     hbox1->addWidget(pb_start);
28     hbox1->addStretch();
29     QHBoxLayout *hbox2 = new QHBoxLayout;
30     hbox2->addStretch();
31     hbox2->addWidget(pb_quit);
32     hbox2->addStretch();
33 \
34     QVBoxLayout *vbox = new QVBoxLayout;
35     vbox->addStretch();
36     vbox->addLayout(hbox1);
37     vbox->addStretch();
38     vbox->addLayout(hbox2);
39     vbox->addStretch();
40     setLayout(vbox);
41     connect(pb_start, SIGNAL(clicked(bool)), this, SLOT(pb_Start()));
42     connect(pb_quit, SIGNAL(clicked(bool)), this, SLOT(pb_Quit()));
43 }
因为就两个按钮,就随便布了一个局,退出游戏最简单,只需要this->close();即可
1 void Widget::pb_Quit()
2 {
3     this->close();
4 }
开始游戏之后,需要删除之前的layout布局,但是不太会,网上找的相关代码,倒是实现了这个功能
1 void Widget::pb_Start()//开始游戏
2 {
3     deleteAllitemsOfLayout(this->layout());//删除布局
4     timestop = 1;//进入游戏
5 }

 

删除布局,即删除控件函数
 1 //清除控件
 2 void Widget::deleteAllitemsOfLayout(QLayout *layout)
 3 {
 4     QLayoutItem *child;
 5     while ((child = layout->takeAt(0)) != nullptr)
 6     {
 7         //setParent为NULL,防止删除之后界面不消失
 8         if(child->widget())
 9         {
10             child->widget()->setParent(nullptr);
11         }else if(child->layout()){
12             deleteAllitemsOfLayout(child->layout());
13         }
14         delete child;
15     }
16 }

 

然后先生成食物的坐标,坐标只需要生成一个点即可
1 //生成食物的坐标
2 void Widget::food()
3 {
4     food_x.append(qrand()%(this->width()/10) * 10);//生成食物的位置坐标
5     food_y.append(qrand()%(this->height()/10) * 10);
6 }
这里用了一个定时器的东西,通过反复绘图实现蛇的运动,不知道这个uodate();有什么作用,哈哈哈,不太会
1 void Widget::timerEvent(QTimerEvent *event)
2 {
3     update();
4 }
蛇的定义是通过链表来实现的,qt有现成的函数可以实现直接在第一个位置上添加和删除某一个位置上的数据。在移动的时候就需要这种函数,添加第一个位置,然后删除最后一个元素即可
 1 void Widget::moveLeft()//左移
 2 {
 3     snake_x.insert(0, (snake_x[0] - 10 + 500)%500);
 4     snake_y.insert(0, snake_y[0]);//长度会加一
 5     deleteLastRectF();
 6 }
 7 void Widget::moveRight()//右移
 8 {
 9     snake_x.insert(0, (snake_x[0] + 10 + 500)%500);
10     snake_y.insert(0, snake_y[0]);//长度会加一
11     deleteLastRectF();
12 }
13 void Widget::moveUp()//上移
14 {
15     snake_x.insert(0, snake_x[0]);
16     snake_y.insert(0, (snake_y[0] - 10 + 500)%500);//长度会加一
17     deleteLastRectF();
18 }
19 void Widget::moveDown()//下移
20 {
21     snake_x.insert(0, snake_x[0]);
22     snake_y.insert(0, (snake_y[0] + 10 + 500)%500);//长度会加一
23     deleteLastRectF();
24 }

 

然后删除结尾的数据
1 //删除结尾数据
2 void Widget::deleteLastRectF()
3 {
4     snake_x.removeAt(snake_x.length() - 1);//删除最后一个节点
5     snake_y.removeAt(snake_y.length() - 1);
6 }
这个游戏,我实现了穿墙,所以只有在吃到自己的时候,蛇才会死亡,然后游戏结束
 1 //判断是否游戏结束
 2 bool Widget::isGameover()
 3 {
 4     for(int i=0; i<snake_x.length(); i++){
 5         for(int j=i+1; j<snake_x.length(); j++){
 6             if((snake_x.at(i) == snake_x.at(j)) && (snake_y.at(i) == snake_y.at(j))){
 7                 return true;
 8             }
 9         }
10     }
11     return false;
12 }
是否迟到了食物也很简单
 1 //判断是否吃到食物
 2 void Widget::iseatFood()
 3 {
 4     for(int i = 0; i < food_x.length(); i++)
 5     {
 6         if((snake_x.at(0) == food_x.at(i)) && (snake_y.at(0) == food_y.at(i)))//如果吃到食物
 7         {
 8             food_x.removeAt(i);
 9             food_y.removeAt(i);
10             snake_x.append(snake_x.last());
11             snake_y.append(snake_y.last());
12             break;
13         }
14     }
15 }
移动的时候,需要先判断当断蛇头移动的方向,对应的,按了向上键之后,按向下键,向下键会属于失效的状态,按不动,定义一个moveFlag,移动的时候更改他的值即可
 1 //移动事件
 2 void Widget::keyPressEvent(QKeyEvent *event)
 3 {
 4     switch (event->key()) {
 5     case Qt::Key_Up:
 6         if(moveFlag != 'D') {
 7             moveFlag = 'U';
 8         }
 9         break;
10     case Qt::Key_Down:
11         if(moveFlag != 'U') {
12             moveFlag = 'D';
13         }
14         break;
15     case Qt::Key_Right:
16         if(moveFlag != 'L') {
17             moveFlag = 'R';
18         }
19         break;
20     case Qt::Key_Left:
21         if(moveFlag != 'R') {
22             moveFlag = 'L';
23         }
24         break;
25     }
26 }

 

然后就是画图了,画图这里,定义画家,画笔,设置样式颜色等等,然后就是逻辑问题,我定义了一个timestop判断是否结束绘图,timestop的初始值为0,不进行绘图,开始游戏之后,更改timestop的值为1,开始绘图。通过moveFlag的值来回去当前移动的方向,然后更爱链表中的数据,在后面即开始绘图,画出蛇,画出食物,等等。最后当游戏结束的时候,将我定义的gameover设置为1,相当于最后一次绘图,将蛇死亡之前的画面中心绘制下来,显现在屏幕上,然后游戏结束。

 1 //画图
 2 void Widget::paintEvent(QPaintEvent *event)
 3 {
 4     if(timestop == 1)
 5     {
 6         timerid = startTimer(100);//停0.1秒
 7         //向某一方向一直移动
 8         if(moveFlag == 'U') {
 9             moveUp();
10         }
11         else if(moveFlag == 'D') {
12             moveDown();
13         }
14         else if(moveFlag == 'R') {
15             moveRight();
16         }
17         else if(moveFlag == 'L') {
18             moveLeft();
19         }
20 
21         QPainter painter(this);
22         QPen pen;
23         painter.setPen(pen);
24         pen.setWidth(snake_size);
25 
26         QBrush brs(Qt::green);
27 
28         QFont font("方正舒体",12,QFont::ExtraLight,false);
29         painter.setFont(font);
30         painter.drawText(20,20,QString("当前得分:")+QString("%1").arg(snake_x.length()-4));
31 
32         painter.setBrush(brs);
33         painter.drawEllipse(snake_x.at(0)-2, snake_y.at(0)-2, snake_size+4, snake_size+4);
34 
35         for (int i = 1; i < snake_x.length(); i++) {//画出蛇的初始状态
36             painter.setBrush(brs);
37             //painter.drawRect(snake_x.at(i), snake_y.at(i), snake_size, snake_size);
38             painter.drawEllipse(snake_x.at(i), snake_y.at(i), snake_size, snake_size);
39         }
40 
41         food_t++;
42         if(food_t == 20)//2秒生成一次食物
43         {
44             food();
45             food_t = 0;
46         }
47         for (int i = 0; i < food_x.length(); i++) {//画出食物
48             painter.setBrush(QColor(255,0,0));
49             painter.drawRect(food_x.at(i), food_y.at(i), snake_size, snake_size);
50         }
51         if(isGameover())
52         {
53             timestop = 0;
54             killTimer(timerid);
55             gameover = 1;
56         }
57         iseatFood();
58     }
59     if(gameover == 1)//打印画面
60     {
61         QPainter painter(this);
62         QPen pen;
63         painter.setPen(pen);
64         pen.setWidth(snake_size);
65         QBrush brs(Qt::green);
66 
67         QFont font("方正舒体",12,QFont::ExtraLight,false);
68         painter.setFont(font);
69         painter.drawText(20,20,QString("当前得分:")+QString("%1").arg(snake_x.length()-4));
70         painter.setBrush(brs);
71         painter.drawEllipse(snake_x.at(0)-2, snake_y.at(0)-2, snake_size+4, snake_size+4);
72         for (int i = 1; i < snake_x.length(); i++) {//画出蛇的初始状态
73             painter.setBrush(brs);
74             //painter.drawRect(snake_x.at(i), snake_y.at(i), snake_size, snake_size);
75             painter.drawEllipse(snake_x.at(i), snake_y.at(i), snake_size, snake_size);
76         }
77         for (int i = 0; i < food_x.length(); i++) {//画出食物
78             painter.setBrush(QColor(255,0,0));
79             painter.drawRect(food_x.at(i), food_y.at(i), snake_size, snake_size);
80         }
81         QFont font1("方正舒体",30,QFont::ExtraLight,false);
82         painter.setFont(font1);
83         painter.drawText((this->width()-200)/2,(this->height()-30)/2,QString("GAME OVER!"));
84     }
85 }

 游戏界面

进入游戏

 

 游戏结束

 

 

posted @ 2020-08-14 19:26  五十九  阅读(1574)  评论(1编辑  收藏  举报