目录
贪吃蛇游戏设计思路
具体操作
训练模式
闯关模式
具体代码
main(主函数)
interface.cpp(登录、注册)
interface.h
widget.cpp(训练模式)
widget.h
confirm.cpp(挑战模式)
confirm.h
snake_class.cpp(具体功能)
snake_class.h
总结
贪吃蛇游戏设计思路
整个工程共4个类,各自在不同阶段发挥作用。
1、首先main函数先实例化 interface类,这个类的功能是通过用户输入(注册或登录)进入闯关模式类,或者直接进入训练模式类。
2、两种模式可以分别实例化snake类,并通过snake提供的接口函数实例为该模式的特定对象。同时可以使用信号与槽实现交互。
具体操作
meeting_02
进入游戏,可以选择登录注册,进入闯关模式。或者直接进入训练模式。
训练模式
可以根据自己的实力,输入数据改变游戏难度。
界面长度:蛇移动的范围,数值越小范围越小。
界面宽度:蛇移动的范围,数值越小范围越小。
格子边长:数值越大,格子越大。单步步长越大。
蛇移动速度:数值越大,移动越快。
注意:必须所有格子都填上数据,才能成功。
闯关模式
分数到达一定数值后会提醒进入下一关,同时格子会自动变大,蛇的移动速度会加快。
同样失败后,会提示游戏失败,再重新开始挑战。
具体代码
main(主函数)
#include "interface.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
interface m;
m.show();
return a.exec();
}
interface.cpp(登录、注册)
#include "interface.h"
#include
interface::interface(QWidget *parent) : QWidget(parent)
{
max= new QLabel;
max->setParent(this);
QMovie *m = new QMovie(":/xh.gif");
max->setMovie(m);
m->start();//开始播放背景标签
QFont ft;
ft.setPointSize(17);
this->setMinimumSize(533,300);
this->setMaximumSize(533,300);
te1 = new QLabel ;
le1 = new QLineEdit ;
te1->setFont(ft);
le1->setFont(ft);
te1->setText("账号");
le1->setPlaceholderText("账号:");
te1->setGeometry(135,110,50,30);
le1->setGeometry(185,110,200,30);
te1->setParent(this);
le1->setParent(this);
te2 = new QLabel ;
le2 = new QLineEdit ;
te2->setText("密码");
le2->setPlaceholderText("密码:");
te2->setFont(ft);
le2->setFont(ft);
te2->setGeometry(135,160,50,30);
le2->setGeometry(185,160,200,30);
te2->setParent(this);
le2->setParent(this);
bt1 = new QPushButton("注册") ;
bt2 = new QPushButton ("登录");
bt1->setGeometry(190,210,50,30);
bt2->setGeometry(320,210,50,30);
bt1->setParent(this);
bt2->setParent(this);
bt3 = new QPushButton("训练模式");
bt3->setGeometry(420,210,70,30);
bt3->setParent(this);
//创建数据库
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("namepassdb");
bool ok = db.open();
qDebug()<hide();
connect(bt1,SIGNAL(clicked(bool)),this,SLOT(registered()));//注册按钮触发
connect(bt2,SIGNAL(clicked(bool)),this,SLOT(login()));//登录按钮触发
connect(bt3,SIGNAL(clicked(bool)),this,SLOT(into_training()));//按钮触发进入训练模式
}
void interface::registered()
{
//向入据库中加入一条信息
qDebug()<<"regis";
QString str = QString("insert into nampass values('%0', '%1');").arg(le1->text()).arg(le2->text());
QSqlQuery query;
query.exec(str);
qDebug()<text()).arg(le2->text());
QSqlQuery query;
query.exec(str);
qDebug() << "log"<< str;
while(query.next())
{
qDebug() << query.value(0).toString();
qDebug() << query.value(1).toString();
hide();
g = new confirm;
g->show();
// w = new Widget;
// w->show();
}
}
void interface::into_training()
{
hide();
w = new Widget;
w->show();
}
interface.h
#ifndef INTERFACE_H
#define INTERFACE_H
#include "widget.h"//训练模式
#include "confirm.h"//闯关模式
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class interface : public QWidget
{
Q_OBJECT
public:
explicit interface(QWidget *parent = nullptr);
signals:
void login_successed();
public slots:
void registered();
void login();
void into_training();
private:
QPushButton *bt1;//注册按钮
QPushButton *bt2;//登录按钮 进入挑战模式
QPushButton *bt3;//训练模式
QLineEdit *le1;//账号
QLineEdit *le2;//密码
QLabel *te1;//标签
QLabel *te2;//标签
QLabel *max;//背景
QSqlDatabase db;//数据库
confirm * g;
Widget *w;//具体游戏类指针(训练模式)
};
#endif // INTERFACE_H
confirm.cpp(挑战模式)
#include "confirm.h"
confirm::confirm(QWidget *parent) : QWidget(parent)
{
this->setMaximumSize(770,770);
snake_confirm = new snake_class(w,h,size,seep);
snake_confirm->setMinimumSize(w+w/5,h+h/5);
// snake_confirm->setParent(this);
// snake_confirm->show();
connect(snake_confirm,SIGNAL(send_scor(int)),this,SLOT(get_data(int)));
connect(snake_confirm,SIGNAL(send_feail()),this,SLOT(reopen_game()));//失败游戏重开
la = new QLabel;
la1 = new QLabel;
la3= new QLabel;
la4 = new QLabel;
QFont ft;
ft.setPointSize(23);
la->setFont(ft);
la1->setFont(ft);
la4->setFont(ft);
la3->setFont(ft);
la->setMaximumSize(70,30);
la1->setMaximumSize(70,30);
la3->setMaximumSize(70,30);
la4->setMaximumSize(70,30);
la->setText(0);
la1->setText("分数");
la3->setText("关卡");
la4->setText("1");
connect(snake_confirm,SIGNAL(send_scor(int)),la,SLOT(setNum(int)));
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(la1);
hbox->addWidget(la);
hbox->addWidget(la3);
hbox->addWidget(la4);
hbox->setAlignment(Qt::AlignLeft);
QVBoxLayout *vbox =new QVBoxLayout;
vbox->addWidget(snake_confirm);
vbox->addLayout(hbox);
setLayout(vbox);
mesg = new QMessageBox;
}
void confirm::set_data()//设置关卡数据
{
snake_confirm->set_data(w,h,size,seep);
}
void confirm::get_data(int score)//正常通关
{
this->score = score;
if(this->score == max)
{
i++;
max += max*i;
size += 5;
seep -= 70;
snake_confirm->set_data(w,h,size,seep);
snake_confirm->change_type(-1);
mesg->setText("即将进入下一关");
mesg->setStandardButtons(QMessageBox::Ok);
qDebug() << mesg->exec();
la4->setNum(i);
snake_confirm->change_type(1);
}
}
void confirm::reopen_game()//失败重开(第一关开始)
{
mesg->setText("闯关失败,游戏重新开始");
mesg->setStandardButtons(QMessageBox::Ok);
qDebug() << mesg->exec();
size=15;
seep=300;
max = 3;
i=1;
la4->setText("1");
snake_confirm->set_data(w,h,size,seep);
}
confirm.h
#ifndef CONFIRM_H
#define CONFIRM_H
#include "snake_class.h"
#include
#include
#include
#include
#include
class confirm : public QWidget
{
Q_OBJECT
public:
explicit confirm(QWidget *parent = nullptr);
void set_data();
signals:
void stop();
public slots:
void reopen_game();
void get_data(int);
private:
snake_class *snake_confirm;
int w =600 ;
int h=600;
int size=15;
int seep=300;
int score=0;
int max = 3;
int i =1;
QLabel *la;
QLabel *la1;
QMessageBox *mesg;
QLabel *la3;
QLabel *la4;
};
#endif // CONFIRM_H
snake_class.cpp(具体功能)
#include "snake_class.h"
snake_class::snake_class(int w=500,int h=500,int size=20,int speed =300 , QWidget *parent)
:w(w),h(h),size(size),speed(speed) ,QWidget(parent)
{
score =0 ;//初始分数
m_speech=new QTextToSpeech;
m_speech->pitchChanged(1023);
heand_point = new QPoint(my_rand()*size+size/2,my_rand()*size+size/2);//蛇头初始值随机
ponit =new QPoint(my_rand()*(size)+size/2,my_rand()*(size)+size/2);//食物初始值随机
//ponit =new QPoint(size+size/2,size+size/2);
ver.append(heand_point);//蛇头加入数组
pen.setWidth(size);//设置笔宽
colr1.setRgb(rand() %255,rand()%255,rand()%255);
pen.setColor(colr1);//设置笔的
timeid = new int;
*timeid = startTimer(speed);//定时器开始
setFocusPolicy(Qt::StrongFocus);//键盘可以获取输入
}
void snake_class::paintEvent(QPaintEvent *event)//重绘事件
{
QPainter per(this);//申请一个画家
per.setPen(pen);//把笔给画家
per.drawPixmap(ver[0]->x()-size/2,ver[0]->y()-size/2,size,size,QPixmap(":/20.png"));//画蛇头
// qDebug()<<"tou"<<*ver[0];
for(int i =1 ; i< ver.length();i++)//画蛇
{
str2 = QString(":/%1.png").arg(qrand()%20+21);
per.drawPixmap(ver[i]->x()-size/2,ver[i]->y()-size/2,size,size,QPixmap(str2));//画蛇身
}
per.drawPixmap(ponit->x()-size/2,ponit->y()-size/2,size,size,QPixmap(str));//画食物
// qDebug()<<"to"<<*ponit;
QPainter per2(this);//画家2
colr2.setRgb(rand() %255,rand()%255,rand()%255);
pen2.setColor(colr2);//设置笔的
per2.setPen(pen2);//把笔给画家
for(int i = 0;i<= w/size;i++)//画竖线
{ per2.drawLine(QPoint(i*size,0),QPoint(i*size,h));}
for(int i = 0;i<= h/size;i++)//画横线
{ per2.drawLine(QPoint(0,i*size),QPoint(w,i*size)); }
if((heand_point->x() >= w || heand_point->x() < 0) ||(heand_point->y() >= h || heand_point->y() < 0 ))
{//判断越界
ver.clear();
heand_point->setX(my_rand()*size+size/2);
heand_point->setY(my_rand()*size+size/2);
ver.append(heand_point);
score=0;
key =0;
m_speech->say("喔嚯");
*ponit = QPoint(my_rand()*(size)+size/2,my_rand()*(size)+size/2);//食物初始值随机
emit send_scor(score);
emit send_feail();
}
if(((heand_point->x()) == ponit->x() ) && ((heand_point->y()) == ponit->y() ))
{//判断是否吃到食物
QPoint *po = new QPoint(0,0);
m_speech->say("加油");
str = QString(":/%1.png").arg(qrand()%12);
ver.append(po);
ponit->setX(my_rand()*size+size/2);
ponit->setY(my_rand()*size+size/2);
emit send_scor(++score);
}
}
int snake_class::my_rand()//随机数
{
// int x= qrand()%((wkey();
key = event->key();
QPoint f(*heand_point);
QPoint f1;
if(event->key() == 16777235){heand_point->setY(heand_point->y()-size);}
if(event->key() == 16777237){heand_point->setY(heand_point->y()+size);}
if(event->key() == 16777234){heand_point->setX(heand_point->x()-size);}
if(event->key() == 16777236){heand_point->setX(heand_point->x()+size);}
for(int i =1 ; i< ver.length();i++)//吃到自己的身体
{
if((ver[i]->x() == heand_point->x()) && (ver[i]->y() == heand_point->y()))
{
ver.clear();
heand_point->setX(my_rand()*size+size/2);
heand_point->setY(my_rand()*size+size/2);
ver.append(heand_point);
score=0;
key =0;
m_speech->say("喔嚯");
*ponit = QPoint(my_rand()*(size)+size/2,my_rand()*(size)+size/2);//食物初始值随机
emit send_scor(score);
emit send_feail();
}
}
for(int i =1;i< ver.length();i++)//蛇身每个节点跟上头
{
f1 = *ver[i];
*ver[i] = f;
f = f1;
}
colr1.setRgb(rand() %255,rand()%255,rand()%255);
pen.setColor(colr1);//设置笔的
// qDebug()<< key;
update();
if(type ==1)
{
*timeid = startTimer(speed);
type =0;
}
}
void snake_class::timerEvent(QTimerEvent *event)//随着时间,蛇朝一个方向运动
{
QPoint f(*heand_point);
if(key == 16777235){heand_point->setY(heand_point->y()-size);}
if(key == 16777237){heand_point->setY(heand_point->y()+size);}
if(key == 16777234){heand_point->setX(heand_point->x()-size);}
if(key == 16777236){heand_point->setX(heand_point->x()+size);}
for(int i =1 ; i< ver.length();i++)//吃到自己的身体
{
if((ver[i]->x() == heand_point->x()) && (ver[i]->y() == heand_point->y()))
{
ver.clear();
heand_point->setX(my_rand()*size+size/2);
heand_point->setY(my_rand()*size+size/2);
ver.append(heand_point);
score=0;
key =0;
m_speech->say("喔嚯");
*ponit = QPoint(my_rand()*(size)+size/2,my_rand()*(size)+size/2);//食物初始值随机
emit send_scor(score);
emit send_feail();
}
}
QPoint f1;
for(int i =1;i< ver.length();i++)
{
f1 = *ver[i];
*ver[i] = f;
f = f1;
}
update();
if(type == -1)
{
killTimer(*timeid);
type =0;
}
}
void snake_class::set_data(int w,int h, int size, int speed)
{
this->w= w;
this->h= h;
this->size = size;
this->speed= speed;
*heand_point = QPoint(my_rand()*size+size/2,my_rand()*size+size/2);//蛇头初始值随机
*ponit = QPoint(my_rand()*(size)+size/2,my_rand()*(size)+size/2);//食物初始值随机
}
void snake_class::change_type(int type)
{
this->type = type;
qDebug()<type;
}
snake_class.h
#ifndef SNAKE_CLASS_H
#define SNAKE_CLASS_H
//#include "snake_class.h"
//#include "confirm.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class snake_class : public QWidget
{
Q_OBJECT
public:
explicit snake_class(int w,int h,int size,int speed ,QWidget *parent = nullptr);
void keyPressEvent(QKeyEvent *event);
void timerEvent(QTimerEvent *event);
void paintEvent(QPaintEvent *event);
void set_data(int w,int h, int size, int speed);
int my_rand();
void change_type(int);
signals:
send_scor(int);
send_feail();
public slots:
private:
QPoint *heand_point;//蛇头
QVector ver;//蛇身
int score;//分数
QPoint *ponit;//食物
int w;//长
int h;//宽
QTextToSpeech *m_speech;//声音
QColor colr1;//颜色
QPen pen;//笔
QPen pen2;//笔
QColor colr2;//颜色
int size;
int key;//键盘值
QString str =":/0.png";//图片地址.食物
QString str2 = ":/20.png";//图片地址,蛇
int speed;//蛇自动移动速度
int type =0;
int *timeid;
// confirm * com;
};
#endif // SNAKE_CLASS_H
总结
优点:
1、贪吃蛇功能全部实现,包括方向控制、自动移动、越界、咬到自身等。
2、实现不同模式,训练与挑战模式。
3、封装了不同的类,具有一定代码复用性。
缺点:
1、界面ui迫切需要优化。
2、代码逻辑性不高,可以将几个大类划分为更小的类。比如将具体功能类划分为:蛇类、食物类。这样代码复用性、移植性更高。
展望新版本:
1、可以编写服务器与客户端,实现多蛇共舞。