一,游戏功能
1,创建定时器,利用槽函数定时刷新游戏界面
2,通过按键方式,实现蛇的方向控制( ↑ ↓ ← → )
3,通过按键方式,实现游戏的开始和暂停( 空格键 )
4,方块链表表示蛇,在首尾增加或删除方块,实现蛇的移动
5,判断蛇头是否碰壁或者咬到自己导致游戏结束
6,随机出现食物方块,判断蛇头是否和食物位置重合
7,蛇每吃到一个食物(蛇头和食物重合),得分+1,长度+1
8,把游戏背景图、蛇、食物、分数等元素画入游戏界面
注:以上操作都局限于单文件,还未将蛇、食物等分出单独的类进行操作
二,操作演示
1,游戏开始界面
2,游戏进行页面
3,游戏结束页面
三,附源代码
1,widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
typedef enum Direct{
//枚举类型 上下左右
DIR_LEFT,
DIR_RIGHT,
DIR_UP,
DIR_DOWN
}dir_t;
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void addUp();//上方添加长度
void addDown();//下方添加长度
void addLeft();//左边添加长度
void addRight();//右边添加长度
bool checkMeet();//判断是否咬到自己
void keyPressEvent(QKeyEvent *event);//处理按键事件 上下左右 空格键
void paintEvent(QPaintEvent *event);//画家事件 用于画蛇和食物 及背景
void addNewFood();//当食物被吃掉后 添加食物
private:
Ui::Widget *ui;
bool gameStart = false;//判断游戏是否开始
QTimer *timer;//创建定时器
int time = 200;//超时时间间隔100ms
bool meet = false;//判断蛇头是否碰壁
int score = 0;//记录分数
QRectF food;//食物小方块
QList snake;//链表表示 蛇身体,由一串小方格组成
int nodeWidth = 20;//定义小方块的宽度
int nodeHeight = 20;//定义小方块的长度
dir_t moveFlag = DIR_UP;//蛇当前的移动方向
int window_length = 1200;
int window_width = 800;
protected slots:
void timeout();//槽函数 超时处理
};
#endif // WIDGET_H
2,widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
timer = new QTimer();//使用定时器
//信号槽(信号和对应的函数关联起来) 定时器有一个timeout信号
connect(timer,&QTimer::timeout,this,timeout);//超时函数处理,实现蛇的移动
//connect(timer,SIGNAL(timeout()),this,SLOT(timeout()));//旧的写法
this->setFixedSize( window_length,window_width);//定义窗口初始大小
//resize(window_length,window_width);//没有固定窗口大小
//初始化蛇的长度
QRectF head(window_length/2,window_width/2,nodeWidth,nodeHeight);//蛇头
snake.append(head);//画出蛇的第一个方块(尾部插入)
addUp();//上方插入,增加蛇身
addUp();
addUp();
addUp();
addUp();
addUp();
addUp();
//增加初始的食物
addNewFood();
}
Widget::~Widget()
{
delete ui;
}
//超时函数
void Widget::timeout()
{
int flag = 1;//判断有没有重合(吃到食物)
if(snake[0].intersects(food))//蛇头和食物
{
flag++;//吃到加一,下面循环的时候多一次,增加一个长度
addNewFood();//添加新的食物
score++;//分数增加
}
while(flag--)//一次或者两次
{
switch (moveFlag) {
case DIR_UP://上面加一个
addUp();
break;
case DIR_DOWN://下面加一个
addDown();
break;
case DIR_LEFT://左边加一个
addLeft();
break;
case DIR_RIGHT://右边加一个
addRight();
break;
default:
break;
}
}
snake.removeLast();//头部添加了一个(或两个),尾部减少一个,实现蛇的移动
update();//直接调用paintEvent刷新页面***paintEvent***的调用
// 每当内容需要更新时,都会调用虚拟paintEvent()函数。然后,重新实现
// 此函数的子类可以简单地在窗口上打开 QPainter。
}
//上方添加长度
void Widget::addUp()
{
QPointF leftTop;//左上角的点坐标
QPointF rightBotom;//右下角的点坐标
if(snake[0].y()-nodeHeight < 0)//蛇头碰到 上墙壁
{
meet = true;//碰壁变量为真
}
else
{
leftTop = QPointF(snake[0].x(),snake[0].y()-nodeHeight);//左上角
rightBotom = snake[0].topRight();//右下角
}
snake.insert(0,QRectF(leftTop,rightBotom));//插入蛇的新节点(蛇头)
}
//下方添加长度
void Widget::addDown()
{
QPointF leftTop;//左上角的点坐标
QPointF rightBotom;//右下角的点坐标
if(snake[0].y()+nodeHeight*2 > window_width)//蛇头碰到 下墙壁
{
meet = true;//碰壁变量为真
}
else
{
leftTop = QPointF(snake[0].x(),snake[0].y()+nodeHeight);//左上角
rightBotom = QPointF(snake[0].x()+nodeWidth,snake[0].y()+nodeHeight*2);//右下角
}
snake.insert(0,QRectF(leftTop,rightBotom));//插入蛇的新节点(蛇头)
}
//左边添加长度
void Widget::addLeft()
{
QPointF leftTop;//左上角的点坐标
QPointF rightBotom;//右下角的点坐标
if(snake[0].x()-nodeWidth < 0)//蛇头碰到 左墙壁
{
meet = true;//碰壁变量为真
}
else
{
leftTop = QPointF(snake[0].x()-nodeWidth,snake[0].y());//左上角
rightBotom = QPointF(snake[0].x(),snake[0].y()+nodeHeight);//右下角
}
snake.insert(0,QRectF(leftTop,rightBotom));//插入蛇的新节点(蛇头)
}
//右边添加长度
void Widget::addRight()
{
QPointF leftTop;//左上角的点坐标
QPointF rightBotom;//右下角的点坐标
if(snake[0].x()+nodeWidth*2 > window_length)//蛇头碰到 右墙壁
{
meet = true;//碰壁变量为真
}
else
{
leftTop = QPointF(snake[0].x()+nodeWidth,snake[0].y());//左上角
rightBotom = QPointF(snake[0].x()+nodeWidth*2,snake[0].y()+nodeHeight);//右下角
}
snake.insert(0,QRectF(leftTop,rightBotom));//插入蛇的新节点(蛇头)
}
//判断蛇头是否咬到自己 或者 碰壁
bool Widget::checkMeet()
{
if(meet == true)//判断蛇头是否碰壁
return true;
for(int i=0;ikey())//传入的按键信号
{
case Qt::Key_Up:
if(moveFlag != DIR_DOWN)//当前方向不是下,就改变为上
moveFlag = DIR_UP;
break;
case Qt::Key_Down:
if(moveFlag != DIR_UP)//当前方向不是上,就改变为下
moveFlag = DIR_DOWN;
break;
case Qt::Key_Left:
if(moveFlag != DIR_RIGHT)//当前方向不是右,就改变为左
moveFlag = DIR_LEFT;
break;
case Qt::Key_Right:
if(moveFlag != DIR_LEFT)//当前方向不是左,就改变为右
moveFlag = DIR_RIGHT;
break;
case Qt::Key_Space://空格键
if(gameStart == false)
{
gameStart = true;//如果游戏暂停,则开始
timer->start(time);//超时时间间隔,刷新
}
else
{
gameStart = false;//如果游戏已经开始,则暂停
//timer->stop();//游戏暂停,移到打印字体的语句中
}
break;
default:
break;
}
}
//添加食物
void Widget::addNewFood()
{
food = QRectF(//除以20再乘以18是为了 使得随机数都是长宽的
//公因数(等于蛇方块的大小),保证食物和蛇头是对齐的
qrand()%(this->width()/20)*20,//x坐标
qrand()%(this->height()/20)*20,//y坐标
nodeWidth,
nodeHeight);
}
void Widget::paintEvent(QPaintEvent *event)
{
//背景图片
QPainter painter(this);//定义这个窗口的画家
QPen pen;//笔
QBrush brush;//画刷
QPixmap pix;//背景图片
pix.load(":/BGP.png");//加载背景图片
painter.drawPixmap(0,0,window_length,window_width,pix);//画出背景图
//改变画笔和画刷的样式
pen.setColor(Qt::black);
brush.setColor(Qt::green);
brush.setStyle(Qt::SolidPattern);
painter.setPen(pen);
painter.setBrush(brush);
//画蛇
for(int i=0;istop();//游戏结束 计时器停止
}
if(gameStart == false)//游戏暂停的时候
{
QFont font("宋体",30,QFont::ExtraBold,false);//设置字体样式
painter.setFont(font);
painter.drawText(0,//x坐标
nodeHeight*7,//y坐标
QString("欢迎试玩"));
timer->stop();
}
pen.setColor(Qt::red);
brush.setColor(Qt::red);
painter.setPen(pen);
painter.setBrush(brush);
QFont font("宋体",30,QFont::ExtraBold,false);
painter.setFont(font);
painter.drawText(0,nodeHeight*3,
QString("分数:"+QString::number(score)));
//将分数转化为字符串,然后输出
QWidget::paintEvent(event);//防止报错:未使用的参数“event”
}
参考视频在这里