开发平台:Qt Creator 5.9.1
项目描述:贪吃蛇小游戏,采取建立不同的界面类(mainWidget,开始界面,和GameWidget,游戏界面,通过接收事件利用信号和槽,来进行界面切换。通过捕扣用户的按键事件。重写窗口对象的keyPresseEvent虚函数控制snake获取食物使自己长大。蛇每次吃到食物时,节点变多一节, 蛇节点数是不固定。用QList来存放每个节点的数据。每个节点都有x, y坐标,用QPoint类对象存放。需要定时移动蛇,可用定时器QTimer。定时器对象超时会发出timeout信号,需要把这个信号连接到时间处理的槽函数。并调用窗口对象的repaint成员触发paintEvent。通过QMessageBox消息框决定游戏是否继续。
QMessageBox消息框的四种用法:
- d MainWindow::on_info_clicked()
- {
-
- QMessageBox::information(this, "Title", "Text");
- }
-
- void MainWindow::on_question_clicked()
- {
-
- QMessageBox::StandardButton reply;
- reply = QMessageBox::question(this, "Title", "Do you like cat?", QMessageBox::Yes | QMessageBox::No);
- if(reply == QMessageBox::Yes)
- {
- }
- else
- {
- }
- }
-
- void MainWindow::on_warning_clicked()
- {
-
- QMessageBox::warning(this, "Title", "Text");
- }
-
- void MainWindow::on_pushButton_4_clicked()
- {
- QMessageBox::question(this, "Title", "Do you like cat?", QMessageBox::YesToAll|QMessageBox::Yes|QMessageBox::No);
- }
- class MyMessageBox : public QObject
- {
- public:
- MyMessageBox();~MyMessageBox();
-
- static void ChMessageOnlyOk_Information(QString info)
- {
- QMessageBox msg;
- msg.setWindowTitle(tr("提示"));
- msg.setText(info);
- msg.setStyleSheet("font: 14pt;background-color:rgb( 0,220, 0)");
- msg.setIcon(QMessageBox::Information);
- msg.addButton(tr("确定"),QMessageBox::ActionRole);
- msg.exec();
- }
-
- static void ChMessageOnlyOk_Error(QString info)
- {
- QMessageBox msg;
- msg.setWindowTitle(tr("提示"));
- msg.setText(info);
- msg.setStyleSheet("font: 14pt;background-color:rgb(220, 0, 0)");
- msg.setIcon(QMessageBox::Critical);
- msg.addButton(tr("确定"),QMessageBox::ActionRole);
- msg.exec();
- }
-
- static int ChMessageOkCancel(QString info)
- {
- QMessageBox msg;
- msg.setWindowTitle(tr("提示"));
- msg.setText(info);
- msg.setStyleSheet("color:rgb(220, 0, 0);font: 14pt");
- msg.setIcon(QMessageBox::Information);
- msg.addButton(tr("确定"),QMessageBox::ActionRole);
- msg.addButton(tr("取消"),QMessageBox::ActionRole);
- return msg.exec();
- }
- };
-
-
-
-
- int ret = MyMessageBox::ChMessageOkCancel(tr("是否继续?"));
- if(1 == ret)
- {
-
- }
- else if(0 == ret)
- {
- }
主窗口分析:mainWidget
头文件分析
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include "GameWidget.h"
#include <QWidget>
#include <QIcon>
#include <QPalette>
#include <QBrush>
#include <QPixmap>
#include <QPushButton>
#include <QMessageBox>
#include <QLabel>
#include <QFont>
class mainWidget : public QWidget
{
Q_OBJECT
public:
mainWidget(QWidget *parent = 0);
~mainWidget();
private:
QPushButton *startbtn;
QPushButton *exitbtn;
GameWidget *g;
QLabel *label;
signals:
public slots:
void exitSlot();
void startSlot();
};
#endif
- 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
.cpp文件分析
注释的都是可以在自己的程序中使用的函数
mainWidget::mainWidget(QWidget *parent): QWidget(parent)
{
this->resize(480,270);//resize函数,用来设置mainWidget这个窗口的大小
this->setMaximumSize(480,270);
this->setWindowIcon(QIcon(":/new/prefix1/img/icon.png"));//设置ICON
this->setWindowTitle("贪吃蛇");
QPalette palette;
palette.setBrush(QPalette::Background,QBrush(QPixmap(":/new/prefix1/img/back.jpg").scaled(this->size())));
this->setPalette(palette);
startbtn=new QPushButton(this);
startbtn->setIcon(QIcon(":/new/prefix1/img/start.png"));
startbtn->setIconSize(QSize(75,75));
startbtn->setGeometry(QRect(250,170,75,75));
startbtn->setFlat(true);
exitbtn=new QPushButton(this);
exitbtn->setIcon(QIcon(":/new/prefix1/img/quit.png"));
exitbtn->setIconSize(QSize(70,70));
exitbtn->setGeometry(QRect(350,170,70,70));
exitbtn->setFlat(true);
//设置说明标签
QFont font;
font.setFamily("Consolas");
font.setBold(true);
font.setPixelSize(13);
label=new QLabel(this);
label->setText("游戏说明:贪吃蛇游戏可使用按钮或者w a s d控制蛇的走动");
label->setFont(font);
label->setGeometry(QRect(10,10,400,50));
connect(exitbtn,SIGNAL(clicked()),this,SLOT(exitSlot()));
connect(startbtn,SIGNAL(clicked()),this,SLOT(startSlot()));
}
mainWidget::~mainWidget()
{
delete startbtn;
delete exitbtn;
}
void mainWidget::exitSlot()
{
if(QMessageBox::question(this,"退出游戏","是否退出当前游戏",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes)
{
delete this;
exit(0);
}
}
void mainWidget::startSlot()
{
g=new GameWidget(this);
g->show();
}
//重写resizeEvent
/*void mainWidget::resizeEvent(QResizeEvent *)
{
QPalette palette;
palette.setBrush(QPalette::Background,QBrush(QPixmap("img/back.jpg").scaled(this->size())));
this->setPalette(palette);
startbtn->setGeometry(QRect(this->size().width()-230,this->size().height()-100,75,75));
exitbtn->setGeometry(QRect(this->size().width()-130,this->size().height()-100,70,70));
}*/
- 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
QMessageBox的初始化及效果
void mainWidget::exitSlot()
{
if(QMessageBox::question(this,"退出游戏","是否退出当前游戏",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes)
{
delete this;
exit(0);
}
}
![]()
游戏窗口分析:GameWidgit
游戏窗口是这个程序的主体部分,各种复杂的事件(槽和信号机制),状态的更新,动画、碰撞检测都是在这个类(游戏窗口就是一个继承了普通窗口< Widget >的派生类)中完成的。
头文件分析
#ifndef GAMEWIDGET_H
#define GAMEWIDGET_H
#include <QWidget>
#include <QPalette>
#include <QIcon>
#include <QPushButton>
#include <QPainter>
#include <QDebug>
#include <QTime>
#include <QTimer>
#include <QMessageBox>
#include <QKeyEvent>
#include <QLabel>
#include <QFont>
#include <QSound>
class GameWidget : public QWidget
{
Q_OBJECT
public:
explicit GameWidget(QWidget *parent = 0);
~GameWidget();
void paintEvent(QPaintEvent *);
void keyPressEvent(QKeyEvent *);
private:
QPushButton *upbtn;
QPushButton *leftbtn;
QPushButton *downbtn;
QPushButton *rightbtn;
QPushButton *startbtn;
QPushButton *returnbtn;
int direction;
int snake[100][2];
int snake1[100][2];
int foodcount;
QTimer *timer;
int foodx,foody;
int score;
int level;
QLabel *scorelabel;
QLabel *levellabel;
QLabel *scoreshow;
QLabel *levelshow;
QString str1,str2;
QSound *sound;
QSound *sound1;
signals:
void UpSignal();
void DownSignal();
void LeftSignal();
void RightSignal();
public slots:
void upbtnSlot();
void leftbtnSlot();
void rightbtnSlot();
void downbtnSlot();
void startbtnSlot();
void returnbtnSlot();
void timeoutSlot();
};
#endif // GAMEWIDGET_H
QDebug的说明
.c文件分析
因为GameWidget略长,且有一些部分和MainWidget用法一致,就不放上来全部代码(想要自己运行的可以从之前附上的链接下载,记得只适用于QT5)
注: 需要include相应的库文件
1.如何让你的游戏发出声音
首先要载入音频
sound=new QSound(":/listen/img/5611.wav");
sound1=new QSound(":/listen/img/die.wav");
在什么时候播放声音,使用if语句判断,一般是一个可以update()的槽函数中(本例为timeoutSlot(),见下文)
sound->play();
2.如何即时显示得分
str1=QString::number(score);
scoreshow=new QLabel(this);
scoreshow->setFont(font);
scoreshow->setGeometry(QRect(385,1,60,30));
scoreshow->setText(str1);
3.如何设置交互(信号和槽机制)
connect(leftbtn,SIGNAL(clicked()),this,SLOT(leftbtnSlot()));
connect(rightbtn,SIGNAL(clicked()),this,SLOT(rightbtnSlot()));
connect(upbtn,SIGNAL(clicked()),this,SLOT(upbtnSlot()));
connect(downbtn,SIGNAL(clicked()),this,SLOT(downbtnSlot()));
connect(startbtn,SIGNAL(clicked()),this,SLOT(startbtnSlot()));
connect(returnbtn,SIGNAL(clicked()),this,SLOT(returnbtnSlot()));
connect(this,SIGNAL(UpSignal()),upbtn,SLOT(click()));
connect(this,SIGNAL(DownSignal()),downbtn,SLOT(click()));
connect(this,SIGNAL(LeftSignal()),leftbtn,SLOT(click()));
connect(this,SIGNAL(RightSignal()),rightbtn,SLOT(click()));
connect的使用
connect(发射信号的类,SIGNAL(信号函数),接收信号的类,SLOT(槽函数));
按钮操作的实现
QPushButton这个类中有click()的信号函数和槽函数,如果发生了鼠标点击事件,被点击的Button就发射click()信号,因为我们用connect将这个信号和GameWidget中的槽函数(比如leftbtnSlot())连接起来,GameWidget就能接收到这个信号并且调用相应的槽函数。
键盘操作同理,因为KeyPressEvent是我们自己写在GameWidget类中,所以为 ‘this’ 指针。这次connect是把键盘事件和按钮联系了起来。
4.如何设置随机数
QTime t
t= QTime::currentTime()
qsrand(t.msec()+t.second()*1000)
5.如何update程序
timer=new QTimer(this);
timer->setInterval(500);
connect(timer,SIGNAL(timeout()),this,SLOT(timeoutSlot()));
该程序设置了一个timer(一个定时触发器,参考这里),并且设置了时间间隔,每过一次时间间隔发送一个timeout()信号。
而GameWidget类中定义了timeoutSlot()槽函数,每过一定时间间隔被调用,执行update()操作,来更新界面状态,下文会讲
6.(重点)timeoutSlot分析
因为这一段很长,也只写出代码结构(需要完整代码见开头链接)
void GameWidget::timeoutSlot()
{
memcpy(snake1,snake,sizeof(snake));
for(int i=foodcount;i>=1;i--)
{
snake[i][0]=snake[i-1][0];
snake[i][1]=snake[i-1][1];
}
switch(direction)
{
case UP:snake[0][1]--;break;
case DOWN:snake[0][1]++;break;
case LEFT:snake[0][0]--;break;
case RIGHT:snake[0][0]++;break;
}
this->update();
}
- 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
memcpy辅助理解
update辅助理解
其中的碰撞检测机制是通过判断xy坐标是否相同来进行的。
Qt工具书上似乎有其他碰撞检测的办法,挖个坑
7.(重点)paintEvent分析
函数声明
void GameWidget::paintEvent(QPaintEvent *);
要定义一个绘制器painter
QPainter painter(this);
画一个小方格(长方形)的代码(该游戏画了很多很多小方格),只摘取一个:j,i是坐标,20, 20是方格的大小
painter.drawRect(j,i,20,20);
读取素材绘制
foodx要乘20的原因是这是相对坐标,而不是绝对坐标
painter.drawImage(foodx*20,foody*20,QImage(":/new/prefix1/img/apple.png").scaled(QSize(20,20)));
部分代码
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->setWindowOpacity(0.8);
this->setWindowTitle("贪吃蛇");
num_initialize();
snake.append(QPoint(5,5));
startTimer(500);
// m_timer = new QTimer(this);
// connect(m_timer, SIGNAL(timeout()), this, SLOT(timerEvent(QTimerEvent)));
// m_timer->start(1000);
}
void Widget::timerEvent(QTimerEvent *e)
{
//trans
QPoint newhead = snake.at(0);
switch(direct)
{
case 0:
newhead.setY(newhead.y()-1); break;
case 1:
newhead.setY(newhead.y()+1); break;
case 2:
newhead.setX(newhead.x()-1); break;
case 3:
newhead.setX(newhead.x()+1); break;
}
snake.push_front(newhead);
// if(snake_sit==fruit_sit)
// { flag=0;
// snake_sit=0;fruit_sit=1;
// create_fruit();
// }
// if(flag==1){
// snake.pop_back(); //更新
// }
// flag=1;
if(snake.contains(pos)) create_fruit();
else snake.pop_back(); //更新
// snake.contains(pos); //获取果实坐标
if(GameOver())
{ Over();
return ;}
if(flag==0)
update();
}
void Widget::keyPressEvent(QKeyEvent *e)
{
if(e->key()==Qt::Key_Up) direct=0;
if(e->key()==Qt::Key_Down) direct=1;
if(e->key()==Qt::Key_Left) direct=2;
if(e->key()==Qt::Key_Right) direct=3;
}
void Widget::paintEvent(QPaintEvent *e)
{
// pos=mapFromGlobal(QCursor::pos());
// qDebug()<<pos;
// m_startX=pos.x(); qDebug()<<pos.x();
// m_startY=pos.y();
// update();
QPainter frame(this); //边框
QPen Fpen(Qt::magenta);
Fpen.setWidth(8);
frame.setPen(Fpen);
frame.drawRect(frame_size,frame_size,frame_w*pen_size,frame_h*pen_size);
QPainter p(this);
QPen pen(QColor(Qt::darkGreen));
p.setBrush(Qt::blue);
pen.setWidth(15); //画笔宽度
p.setPen(pen); //交
for(int i=0;i<snake.length(); i++)
{
p.drawRect(frame_size+snake.at(i).x()*pen_size,
frame_size+snake.at(i).y()*pen_size,
1,1); //1,1
qDebug()<<snake.at(i).x()<<" y:"<<snake.at(i).y();
//if(i==2) flag=1;
snake_sit=snake.at(i).x();
}
// p.drawPoint(m_startX, m_startY); //鼠标脱线
// fruit.drawEllipse(frame_size+pos.x()*pen_size,
// frame_size+pos.y()*pen_size,
// pen_size,pen_size);
// QPainter fruit(this); //食物
QPainter fruit(this);
QPen pen1(QColor(Qt::green));
fruit.setBrush(Qt::red);
pen1.setWidth(1); //画笔宽度
fruit.setPen(pen1); //交
// frame.drawEllipse(frame_size+pos.x()*pen_size,
// frame_size+pos.y()*pen_size,
// pen_size,pen_size);
frame.drawRect(frame_size+pos.x()*pen_size,
frame_size+pos.y()*pen_size,
15,10);
qDebug()<<"f:"<<pos.x()+5<<" : "<<pos.y()+5;
}
void Widget::create_fruit()
{
pos.setX(rand()%frame_w);
pos.setY(rand()%frame_h);
if(snake.contains(pos)) create_fruit();
}
bool Widget::GameOver()
{
int X=snake.front().x();
int Y=snake.front().y();
if(X<0||X>frame_w-1||Y<0||Y>frame_h-1)
{ flag=1; return true;}
for(int i=3;i<snake.size();i++)
{
if(snake[i]==snake.front())
{ flag=1; return true;}
}
return false;
}
void Widget::Over()
{
flag=1;
QMessageBox::information(this,"failed","^_^ 你死了!");
}
void Widget::num_initialize()
{
flag =0;
pen_size =20;
frame_size=50;
frame_w =15;
frame_h =20;
direct =3; //keyboard
snake_sit=0;
fruit_sit=1;
}
Widget::~Widget()
{
}
![](https://img-blog.csdn.net/20171031113434640?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQ1Fib3libG9n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)