设计思路
蛇身和豆子:使用了QList容器,其中的对象都是QRectf矩形;QList< QRectF >
移动:先判断当前方向,和蛇头方块坐标,判断该坐标是否会撞到蛇身、边框,没撞到则往
QList首插入一个节点,尾部元素移除,完成前进的效果;
如果移动后碰到食物(即蛇头坐标和某一个食物坐标重合),则不移除尾部元素,实现蛇身增
长一格
新建一个Qt项目
创建第一个登录窗口widget
widget.h
#include <QWidget>
#include <QSqlDatabase>//数据库类,需要加库
#include "snakesgame.h"//游戏窗口头文件
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
signals:
void login_successed(QString);//登录成功的信号
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_2_clicked();//注册按钮的槽函数
void on_pushButton_clicked();//登录按钮的槽函数
private:
Ui::Widget *ui;
snakesgame * s;//游戏窗口
QSqlDatabase db;//申明数据库
};
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QSqlQuery>//SQL语句类
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);//按钮和行编辑框使用的拖拽
s=new snakesgame;//定义游戏窗口
s->hide();//游戏窗口隐藏
this->setFixedSize(800,600);//设置当前窗口的固定大小
connect(this, SIGNAL(login_successed(QString)), s, SLOT(sqlcreate(QString)));//信号与槽机制,登陆成功调用游戏界面的槽函数
//创建数据库
db = QSqlDatabase::addDatabase("QSQLITE");//选择使用哪一种数据库"QSQLITE",创建一个类型为QSQLITE的数据库
db.setDatabaseName("namepassdb");//数据库的名字为namepassdb
bool ok = db.open();
//创建用户名+密码表
QString str("create table namepass(name, pass,score);");//创建数据库表的SQL语句
QSqlQuery query;//辅助执行SQL语句的类
query.exec(str);//执行SQL语句
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_2_clicked()//注册
{
//向数据库中加入一条信息
QString str = QString("insert into namepass(name, pass,score) values('%0', '%1','0');").arg(ui->lineEdit->text()).arg(ui->lineEdit_2->text());//加入信息的SQL语句
QSqlQuery query;
query.exec(str);
}
void Widget::on_pushButton_clicked()//登录
{
//去数据库中查询一条信息
QString str = QString("select * from namepass where name='%0' and pass='%1';").arg(ui->lineEdit->text()).arg(ui->lineEdit_2->text());
QSqlQuery query;
query.exec(str);
while(query.next())//如果查到信息返回正值
{
// qDebug() << query.value(0).toString();//显示
// qDebug() << query.value(1).toString();
emit login_successed(ui->lineEdit->text());//调用信号
hide();//登录界面隐藏
s->show();//游戏窗口显示
}
}
创建第二个游戏窗口snakesgame继承于Qwidget
snakesgame.h
#include <QWidget>
#include <QPaintEvent>
#include<QKeyEvent>
#include<QList>
#include<QTimer>
#include<QLCDNumber>
#include<QLabel>
class snakesgame : public QWidget
{
Q_OBJECT
public:
explicit snakesgame(QWidget *parent = nullptr);
~snakesgame();
void paintEvent(QPaintEvent *event);//绘画事件
void keyPressEvent(QKeyEvent *event);//键盘按压事件
void addTopRectF();//向上的方向增加节点,造成向上移动的效果
void addDownRectF();//向下的方向增加节点,造成向下移动的效果
void addRightRectf();//向右的方向增加节点,造成向右移动的效果
void addLeftRectf();//向左的方向增加节点,造成向左移动的效果
void deleteLastRectf();//删掉最后一个节点
bool snakedead();//游戏结束
public slots:
void game_timeout();
void flash_timeout();
void food_timeout();
void sqlcreate(QString name);//接受登陆界面的槽函数
private:
QString id;//游戏id
QList<QRectF> snake;//贪吃蛇本体,使用了QList容器,其中的对象都是QRectf矩形
QList<QRectF> food;//豆子
int snakefoodWidth = 10;//蛇和豆子的宽度
int snakefoodHeight = 10; //蛇和豆子的高度
int moveFlage=Up;//移动方向
enum{Up,Down,Left,Right};
QTimer * gametimer;//游戏刷新时间定时器
QTimer * foodtimer;//食物事件刷新定时器
QTimer * timer;//游戏时间定时器
int time=100;//游戏刷新时间
int Countdown=60;//游戏时间
int score;//分数
QLCDNumber *lcd;//显示时间
QLabel * lb1;//显示提示
QLabel * lb2;//显示历史最高分
bool gameStart=false;//游戏是否开始
bool gameOver=false;//游戏是否结束
};
snakesgame.cpp
snakesgame构造函数
#include "snakesgame.h"
#include<QPainter>
#include<QPen>
#include <QtGlobal>
#include <QPointF>
#include<Qtime>
#include <QSqlQuery>
snakesgame::snakesgame(QWidget *parent) : QWidget(parent)
{
setWindowTitle("贪吃蛇");
setFixedSize(800,600);
lb1=new QLabel;
QFont font("黑体",12,QFont::ExtraLight,false);
lb1->setFont(font);
lb1->setParent(this);
lb1->setGeometry(610,150,190,300);
lb1->setText("Tab:开始游戏\nSpace:暂停游戏\nSpace:恢复游戏\nAlt:游戏结束之\n后重新开始");
lb2=new QLabel;
lb2->setGeometry(630,10,170,30);
lb2->setFont(font);
lb2->setParent(this);
snake.append(QRectF(300,300,snakefoodHeight,snakefoodWidth));//在对应的坐标添加节点
addTopRectF();
addTopRectF();
food.append(QRectF(50,50,snakefoodHeight,snakefoodWidth));
gametimer=new QTimer;
//gametimer->start(time);
connect(gametimer,SIGNAL(timeout()),this,SLOT(flash_timeout()));
foodtimer=new QTimer;
//foodtimer->start(time);
connect(foodtimer,SIGNAL(timeout()),this,SLOT(food_timeout()));
timer=new QTimer;
lcd=new QLCDNumber;
lcd->display(Countdown);
lcd->setGeometry(630,80,150,50);
lcd->setParent(this);
connect(foodtimer,SIGNAL(timeout()),this,SLOT(game_timeout()));
}
与登陆界面联通的槽函数
void snakesgame::sqlcreate(QString name)//接受登陆界面传过来的id
{
id=name;
QString str = QString("select * from namepass where name='%0';").arg(id);//在数据库表里查找此id
QSqlQuery query;
query.exec(str);
while(query.next())
{
score=query.value(2).toString().toInt();//记录最高分
QString str1=QString("历史最高分:%0").arg(query.value(2).toString());
lb2->setText(str1);//在标签页显示
}
}
绘图事件
void snakesgame::paintEvent(QPaintEvent *event)//绘图事件
{
QPainter p1(this);
QBrush brush1;
brush1.setColor(QColor(128,128,128,127));
brush1.setStyle(Qt::SolidPattern);
p1.setBrush(brush1);
p1.drawRect(0,0,600,600);//游戏框
QPainter p2(this);
QPen pen2(QColor(255, 0, 0, 255));
QBrush brush2;
brush2.setColor(QColor(255, 0, 0, 255));
brush2.setStyle(Qt::Dense2Pattern);
pen2.setWidth(5);
p2.setPen(pen2);
p2.setBrush(brush2);
for(int i = 0;i < snake.length();i++){
p2.drawRect(snake.at(i));
}//画蛇
pen2.setColor(QColor(0, 255, 0, 255));
brush2.setColor(QColor(0, 255, 0, 255));
brush2.setStyle(Qt::SolidPattern);
p2.setBrush(brush2);
p2.setPen(pen2);
for(int i = 0;i < food.length();i++){
p2.drawEllipse(food.at(i));
}//画豆子
QFont font("黑体",12,QFont::ExtraLight,false);//设置字体
pen2.setColor(Qt::black);
p2.setPen(pen2);
p2.setFont(font);
p2.drawText(630,60,QString("当前得分:%0").arg(snake.length()-3));
if(snakedead()|| Countdown==0)//游戏结束,蛇死或时间结束
{
QFont font2("方正舒体",30,QFont::ExtraLight,false);
p2.setFont(font2);
p2.drawText(120,300,QString("GAME OVER!"));
p2.drawText(120,400,QString("你的分数:%0").arg(snake.length()-3));
if((snake.length()-3)>score)//如果分数大于历史最高分就更新数据库表
{
QString str = QString("update namepass set score='%0' where name='%1';").arg(snake.length()-3).arg(id);
QSqlQuery query;
query.exec(str);
}
gametimer->stop();
foodtimer->stop();
gameOver = true;
}
//QWidget::paintEvent(event);//加不加都不影响
}
键盘按压事件
void snakesgame::keyPressEvent(QKeyEvent *event)//键盘按压事件
{
event->accept();
switch(event->key())
{
case Qt::Key_Up:
if(moveFlage!=Down)//按键方向不能和现在移动方向相反
moveFlage=Up;
break;
case Qt::Key_Down:
if(moveFlage!=Up)
moveFlage=Down;
break;
case Qt::Key_Right:
if(moveFlage!=Left)
moveFlage=Right;
break;
case Qt::Key_Left:
if(moveFlage!=Right)
moveFlage=Left;
break;
case Qt::Key_Tab://开始
gametimer->start(time);
foodtimer->start(time*30);
timer->start(10*time);
break;
case Qt::Key_Alt://重新开始
if(gameOver){
snake.clear();
food.clear();
moveFlage = Up;
snake.append(QRectF(300,300,snakefoodWidth,snakefoodHeight));
addTopRectF();
addTopRectF();
//首先生成一个奖励节点
food.append(QRectF(100,100,snakefoodWidth,snakefoodHeight));
gametimer->start(time);
foodtimer->start(time*30);
timer->start(10*time);
sqlcreate(id);
gameOver = false;
}
break;
case Qt::Key_Space://暂停和恢复游戏
if(gameStart && !gameOver){//恢复游戏
gametimer->start(time);
foodtimer->start(time*30);
timer->start(10*time);
gameStart = false;
}else if(!gameStart && !gameOver){//暂停
gametimer->stop();
foodtimer->stop();
timer->stop();
gameStart = true;
}
break;
default:break;
}
}
界面刷新
void snakesgame::flash_timeout()//界面刷新
{
int flag=1;
for(int i=0;i<food.length();i++)
{
if(food.at(i).contains(snake.at(0).topLeft()+QPointF(snakefoodWidth/2,snakefoodHeight/2)))//吃到豆子,判断方法是蛇头的中心坐标碰到了任何一个豆子
{
if(food.at(i).width()>snakefoodWidth){//大的豆子
flag+=1;//奖励分数+1
Countdown+=1;
}
flag+=1;
Countdown+=1;
food.removeAt(i);
break;
}
}
while(flag--)//向对应的方向添加吃到的节点数+1
{
switch (moveFlage) {
case Up:
addTopRectF();
break;
case Down:
addDownRectF();
break;
case Right:
addRightRectf();
break;
case Left:
addLeftRectf();
break;
default:
break;
}
}
deleteLastRectf();//删掉蛇的最后一个节点,因为一开始flag就是1,而且也能表现出移动的效果
update();
}
移动
void snakesgame::addTopRectF()//向上移动
{
// if(snake.at(0).y()-snakefoodHeight<0){
// snake.insert(0,QRectF(QPointF(snake.at(0).x(),600-snakefoodHeight),
// QPointF(snake.at(0).x()+snakefoodWidth,600)));//蛇可以穿墙的情况
// }else{
snake.insert(0,QRectF(snake.at(0).topLeft()+QPointF(0,-snakefoodHeight),snake.at(0).topRight()));//在位置0添加矩形结点
// }
}
void snakesgame::addDownRectF()//向下移动
{
// if(snake.at(0).y()+snakefoodHeight*2 > 600){
// snake.insert(0,QRectF(QPointF(snake.at(0).x(),snakefoodHeight),
// QPointF(snake.at(0).x()+snakefoodWidth,0)));
// }else{
snake.insert(0,QRectF(snake.at(0).bottomLeft(),snake.at(0).bottomRight()+QPointF(0,snakefoodHeight)));
// }
}
void snakesgame::addLeftRectf()//向左移动
{
// if(snake.at(0).x()-snakefoodWidth < 0){
// snake.insert(0,QRectF(QPointF(600-snakefoodWidth,snake.at(0).y()),
// QPointF(600,snake.at(0).y()+snakefoodHeight)));
// }else{
snake.insert(0,QRectF(snake.at(0).topLeft()+QPointF(-snakefoodWidth,0),snake.at(0).bottomLeft()));
// }
}
void snakesgame::addRightRectf()//向右移动
{
// if(snake.at(0).x()+snakefoodWidth*2 > 600){
// snake.insert(0,QRectF(QPointF(0,snake.at(0).y()),
// QPointF(snakefoodWidth,snake.at(0).y()+snakefoodHeight)));
// }else{
snake.insert(0,QRectF(snake.at(0).topRight(),snake.at(0).bottomRight()+QPointF(snakefoodWidth,0)));
// }
}
void snakesgame::deleteLastRectf()
{
snake.removeLast();
}
蛇死亡
bool snakesgame::snakedead()
{
if(snake.at(0).y()-snakefoodHeight<0 || snake.at(0).y()+snakefoodHeight*2 > 600 ||
snake.at(0).x()-snakefoodWidth < 0 || snake.at(0).x()+snakefoodWidth*2 > 600)//撞墙
{
timer->stop(); //暂停定时器
return true;
}
for(int i=0; i<snake.length(); i++){//撞到自己
for(int j=i+1; j<snake.length(); j++){
if(snake.at(i) == snake.at(j)){
timer->stop();
return true;
}
}
}
return false;
}
刷新奖励
void snakesgame::food_timeout()//刷新奖励
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));//生成随机数
if(food.length() > 5){
food.removeAt(qrand()%5);//界面只能有五个奖励
}
food.append(QRectF(qrand()%(600/20)*20,qrand()%(600/20)*20,snakefoodWidth,snakefoodHeight));
if(qrand()%5 == 3){
food.append(QRectF(qrand()%(600/20)*20-5,qrand()%(600/20)*20-5,snakefoodWidth*2,snakefoodHeight*2));//生成大的奖励
}
}
游戏时间
void snakesgame::game_timeout()
{
lcd->display(Countdown);//倒计时
Countdown--;
}
snakesgame::~snakesgame()
{
}
游戏截图
登陆界面:
游戏界面一开始:
重新开始过后的游戏
实现视频
代码链接
百度云链接:https://pan.baidu.com/s/1i7jJbq-_Olz4hZkM0u46vQ
提取码:0802