Qt实现贪吃蛇1:数组实现

前言

最近刚学了链表这一方面的内容,感觉挺强大的,正巧想起以前用数组写过一个贪吃蛇游戏代码,于是打算用两种方法实现它。方法为以下两种:

  • 数组
  • 链表

正文

其实,用数组实现贪吃蛇还算是比较简单的了,为什么?因为不用花太大功夫去考虑蛇身的每个节点之间的关系。同样,在实现蛇的游动、吃食、撞墙、吃到自己等功能时,最核心的部分我觉得是要弄清。

  • 其中 void paintEvent(QPaintEvent *event) 用来在每次重绘时绘制游戏布局、食物的位置、蛇的位置。分别用两个数组表示 食物的位置、蛇的位置
1
2
int food[2];// food[0]表示 x坐标,food[1]表示 y坐标 
int snake[100][2]; //同理,不过这里100相当于最多有100个蛇节点
  • 接着,void keyPressEvent(QKeyEvent * event) 事件用来改变蛇运动的方向。我添加了一个枚举值
1
2
3
4
5
6
7
enum  Direction:int
{
East,
South,
West,
North
};
  • 由于蛇是自己移动的,所以还是创建一个时钟 QTimer 对象。
  • 那么怎么判断蛇是否吃到食物呢,这里其实很简单,可以判断蛇头的x,y坐标是否与食物的x,y坐标相等即可,即if(snake[0][0]==food[0]&&snake[0][1]==food[1]){...}
  • 所以,检测是否撞到墙,也可以这样判断蛇头是否超过了边界 if((snake[0][0]<0||snake[0][1]<0||snake[0][0]>Columns-1||snake[0][1]>Rows-1)){ ... }
  • 检测蛇是否吃到自己,可以 遍历每个蛇节点,判断蛇头 snake[0] 是否等于 某一个蛇身节点 snake[n] 即:蛇头在蛇的身上,表示吃到自己

好了,有了这些思路,那么就可以开始写代码了,可以创建一个Qt widget项目。
代码如下:

dialog.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include<QPalette>
#include<QPushButton>
#include<QLabel>
#include<QPainter>
#include<QDebug>
#include<QKeyEvent>
#include<QTimer>
#include<QTime>
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public slots:
void timeOut();
void btnStartslot();
void btnPauseslot();
protected:
void keyPressEvent(QKeyEvent *);
void paintEvent(QPaintEvent *event);
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void initialized();
void snakeRun();
void checkHItWall();
void checkEatSelf();
enum Direction:int{
East,
South,
West,
North
};
private:
int food[2];//食物坐标
int snake[100][2];//蛇坐标
int eatfoodCount;//吃到的食物总数
int directtion;//蛇头方向
int scores;//得分
int speed;//蛇移动速度
QLabel *lbScroe;
QTimer *timer;
Ui::Dialog *ui;
};
#endif // DIALOG_H

dialog.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#include "dialog.h"
#include "ui_dialog.h"
#include<QMessageBox>
#define Columns 20 //20列
#define Rows 20 //20行
void Dialog::timeOut()
{
snakeRun();
}
void Dialog::btnStartslot()
{
timer->start();
}
void Dialog::btnPauseslot()
{
timer->stop();
}
void Dialog::keyPressEvent(QKeyEvent * event)
{
if(event->key()=='A'){
if(directtion!=West){
directtion=East;
}
}
if(event->key()=='D'){
if(directtion!=East){
directtion=West;
}
}
if(event->key()=='W'){
if(directtion!=South){
directtion=North;
}
}
if(event->key()=='S'){
if(directtion!=North){
directtion=South;
}
}
}
///
/// \brief Dialog::paintEvent
/// \param event
/// 绘图事件主要负责 绘制 蛇
void Dialog::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QPen pen;
pen.setColor(QColor(Qt::darkGray));
painter.setPen(pen);
//绘制游戏布局
for (int row = 0; row < Rows; ++row) {
for (int column = 0; column < Columns; ++column) {
painter.drawRect(row*20,column*20,20,20);
}
}
//绘制食物
painter.fillRect(food[0]*20,food[1]*20,20,20,Qt::red);
//绘制蛇头
painter.fillRect(snake[0][0]*20,snake[0][1]*20,20,20,Qt::green);
//根据食物的多少来绘制蛇身大长度
for (int snakeBody = 1; snakeBody <= eatfoodCount; snakeBody++) {
painter.fillRect(snake[snakeBody][0]*20,snake[snakeBody][1]*20,20,20,QColor(0,100,0));
}
}
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//设置随机数
QTime t=QTime::currentTime();
int s= t.secsTo(QTime(0,0,0,0));
qsrand(s);
//初始化
initialized();
QPushButton *btnStart=new QPushButton(this);
btnStart->setFlat(true);
btnStart->setText("start ...");
btnStart->setGeometry(500,100,250,50);
QPushButton *btnPause=new QPushButton(this);
btnPause->setFlat(true);
btnPause->setText("pause ...");
btnPause->setGeometry(500,180,250,50);
connect(btnStart,SIGNAL(clicked(bool)),this,SLOT(btnStartslot()));
connect(btnPause,SIGNAL(clicked(bool)),this,SLOT(btnPauseslot()));
QLabel *lbshowScroe=new QLabel(this);
lbScroe=new QLabel(this);
lbshowScroe->setText("得分:");
lbScroe->setText(QString::number(scores));
lbshowScroe->setGeometry(500,50,100,50);
lbScroe->setGeometry(600,50,100,50);
this->setFixedSize(800,460);
this->setWindowTitle("贪吃蛇");
//设置时钟
timer=new QTimer(this);
timer->setInterval(speed);
connect(timer,SIGNAL(timeout()),this,SLOT(timeOut()));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::initialized()
{
eatfoodCount=0;
scores=0;
speed=200;
//初始化食物坐标
food[0]=qrand()%Columns;
food [1]= qrand()%Rows;
//初始化蛇头坐标
snake[0][0]=qrand()%Columns;
snake[0][1]=qrand()%Rows;
for (int i = 0; i < eatfoodCount; ++i) {
//判断食物是否出现在蛇的身上
if(food[0]==snake[i][0]&&food[1]==snake[i][1]){
qDebug()<<"the food position is at snake ...";
snake[0][0]=qrand()%Columns;
snake[0][1]=qrand()%Rows;
}
}
//初始化蛇头方向
directtion=qrand()%4;
}
///
/// \brief Dialog::snakeRun
/// 蛇移动
void Dialog::snakeRun()
{
//打印出蛇的每个节点坐标,用于测试
for (int i = 0; i < eatfoodCount; ++i) {
qDebug()<<QString("%1 : (%2-%3)").arg(i).arg(snake[i][0]).arg(snake[i][1]);
}
qDebug()<<endl;
//吃到食物,即 食物 food x,y坐标 等于 蛇头snake[0] x,y坐标
if(snake[0][0]==food[0]&&snake[0][1]==food[1]){
scores+=20; //得分
qDebug()<<"eat the food";
eatfoodCount++;//食物个数++
lbScroe->setText(QString::number(scores));
//产生新的食物位置
food[0]=qrand()%Columns;
food [1]= qrand()%Rows;
}
//变换速度
if(scores>= 50){
speed=100;
timer->setInterval(speed);
}else if(scores>=100){
speed=50;
timer->setInterval(speed);
}
checkHItWall();
checkEatSelf();
//蛇头向前移动一格后,蛇头坐标变了,
//原蛇头的坐标为第一节身体的坐标,
//原第一节身体坐标为现在第二节的坐标,即所有坐标向前移动一格
for (int i = eatfoodCount; i >0; i--) {
snake[i][0]=snake[i-1][0];
snake[i][1]=snake[i-1][1];
}
//根据方向来移动蛇
switch (directtion) {
case East:
snake[0][0]--;
break;
case South:
snake[0][1]++;
break;
case West:
snake[0][0]++;
break;
case North:
snake[0][1]--;
break;
default:
break;
}
/// 更新绘图
update();
}
///
/// \brief Dialog::checkHItWall
/// 是否撞墙
void Dialog::checkHItWall()
{
//即 判断是否超过了边界
if((snake[0][0]<0||snake[0][1]<0||snake[0][0]>Columns-1||snake[0][1]>Rows-1)){
qDebug()<<"Game Over";
timer->stop();
QMessageBox::information(this,"game over","hit the wall");
this->close();
}
}
///
/// \brief Dialog::checkEatSelf
/// 是否吃到自己
void Dialog::checkEatSelf()
{
//遍历每个蛇节点,判断蛇头 snake[0] 是否等于 某一个蛇身节点 snake[n]
for (int n = 1; n <= eatfoodCount; n++) {
if(snake[0][0]==snake[n][0]&&snake[0][1]==snake[n][1]){
qDebug()<<"Game Over;;;";
timer->stop();
QMessageBox::information(this,"game over","eat self");
this->close();
}
}
}

代码中的注释还算是比较详细的吧。。。不过关键还是自己亲手写出来!
现在,来看一下运行结果
img

那么,下次在介绍用链表实现贪吃蛇!