贪吃蛇Greedy Snake
1分析
首先这是一个贪吃蛇界面,由一个长方形边框和一只贪吃蛇组成
默认开局时,贪吃蛇身体只有3个小方块,使用画笔画出
1.1如何移动
对于蛇的移动,有2种方法
- 在一定时间范围内(定时器),未对游戏做出操作(前后左右移动),则整个身体向面朝方向移动一格
- 在一定时间范围内(定时器),未对游戏做出操作(前后左右移动),则身体最前面加上一格方块,身体最后面减去一个方块
这里使用第2种方法
如图,有4个坐标,AC分别是原蛇的头部方格的两个坐标
则新产生的方格的B点为(x1,y1-方格高度);D点(x3,y3-方格高度)
注意
向某个方向增加不能无限增加下去,当触碰到顶部时,我们可以设置条件:
- 死亡
- 穿过从另一个方向开始
这里我们采用第2种方式
1.2向上
如图,此时BD点已经超过x轴,即高度为0的位置
因此我们可以判断:当蛇头部的方格A/C的高度 - 1个方格的高度 < 0 时,则从底部开始进入
-
移动前:x3=x1+蛇宽度;y3=y1
-
移动后:x2=x1,y2=窗口高度-蛇宽度;x4=x2+蛇宽度,y4=y2
1.3向下
同理,向下走只要最下面增加,最上面减少即可
-
移动前:x4=x2+蛇宽度;y2=y4
-
移动后:x1=x2,y1=0;x3=x1+蛇宽度,y3=蛇高度
1.4向左
-
移动前:x3=x2+蛇宽;y3=y2+蛇高
-
移动后:x1=x2-蛇宽,y1=y2;x4=x2,y4=y3
穿越
- x2=窗口宽度-蛇宽,y2=y1
- x3=窗口宽度,y3=y4
1.5向右
-
移动前:x4=x1+蛇宽;y4=y1+蛇高
-
移动后:x2=x1+蛇宽,y2=y1;x3=x4+蛇宽,y3=y4
穿越
- x2=0,y2=y1;
- x3=蛇宽,y3=y4
2如何成长
在界面的不同地方会默认刷新出一个球,蛇吃了这个球身体就会增加一格,同时球消失刷新出现在其他地方
判断球是否被吃掉:即判断蛇的身体是否与球重合,如果重合,增加身体1,同时刷新球出现的位置
3代码
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#define OUTTIME 100 //超时时间
#define SNAKE_WIDTH 20 //蛇的一个方格宽度
#define SNAKE_HEIGHT 20 //蛇的一个方格高度
#include <QWidget>
#include<QKeyEvent>
#include<QDebug>
#include<QTimer>
#include<QPixmap>
#include<QPainter>
#include<random>
#include<QTime>
#include <QRandomGenerator>
/*
0.创建蛇
要求:可以实现前面删除后面增加,可以用LIST链表
1.按键处理
2.使用定时器
2.1关联信号槽
2.2启动定时器
2.3实现对应的超时处理(timeOut)函数
3.渲染图像
*/
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
//定义方向
enum Direction
{
LEFT,
RIGHT,
UP,
DOWN
};
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
//按键处理
void keyPressEvent(QKeyEvent *event);
//添加头部
void addTOP();
void addBUTOM();
void addLEFT();
void addRIGHT();
//绘制界面
void paintEvent(QPaintEvent *event);
//删除尾部
void deleLAST();
//初始化食物
void initFood();
//判断是否碰撞
bool isIntersect();
private:
Ui::Widget *ui;
int moveFlag = UP; //方向标志
bool gameStatu = false;//游戏状态
//bool isIntersect = false;
//定时器
QTimer *timer;
//事物
QRectF food;
//创建蛇
QList<QRectF> snake;
protected slots:
void timeOut();//超时函数
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(450,300);
//使用定时器
timer = new QTimer();
//发出信号
//连接信号
connect(timer,SIGNAL(timeout()),this,SLOT(timeOut()));
//初始化蛇
QRectF rect(this->width()/2,this->height()/2,SNAKE_WIDTH,SNAKE_HEIGHT);
snake.append(rect); //蛇身+1
addTOP(); //蛇身+1
addTOP();
//初始化食物
initFood();
}
Widget::~Widget()
{
delete ui;
}
//按键绑定:控制方向
void Widget::keyPressEvent(QKeyEvent *event)
{
switch(event->key())
{
case Qt::Key_Up:
if(moveFlag != DOWN)
{
moveFlag = UP;
}
break;
case Qt::Key_Down:
if(moveFlag != UP)
{
moveFlag = DOWN;
}
break;
case Qt::Key_Left:
if(moveFlag != RIGHT)
{
moveFlag = LEFT;
}
break;
case Qt::Key_Right:
if(moveFlag != LEFT)
{
moveFlag = RIGHT;
}
break;
case Qt::Key_Space:
if(!gameStatu)
{
gameStatu = true;
//启动定时器
timer->start(OUTTIME);
}
else
{
gameStatu = false;
timer->stop();
}
break;
default:
return;
break;
}
}
//顶部增加
void Widget::addTOP()
{
QPointF leftTop; //左上点
QPointF rightBotom; //右下点
if(snake[0].y() - SNAKE_HEIGHT < 0 ) //如果超过顶部
{
leftTop = QPoint(snake[0].x(),this->height() - SNAKE_HEIGHT);
rightBotom = QPoint(snake[0].x() + SNAKE_WIDTH,this->height());
}
else //未超过顶部
{
leftTop = QPointF(snake[0].x(),snake[0].y() - SNAKE_HEIGHT);
rightBotom = snake[0].topRight();
}
snake.insert(0,QRectF(leftTop,rightBotom));
}
void Widget::addBUTOM()
{
QPointF leftTop;
QPointF rightBotom;
if(snake[0].y() + 2*SNAKE_HEIGHT > this->height() ) //如果超过底部
{
leftTop = QPointF(snake[0].topLeft().x(),0);
// 原x 0
rightBotom = QPointF(snake[0].bottomRight().x(),SNAKE_HEIGHT);
// 右下x+蛇宽 蛇高
}
else //未超过底部
{
leftTop = snake[0].bottomLeft();
rightBotom = snake[0].bottomRight()+QPointF(0,SNAKE_HEIGHT);
}
snake.insert(0,QRectF(leftTop,rightBotom));
}
void Widget::addLEFT()
{
QPointF leftTop;
QPointF rightBotom;
if(snake[0].x()-SNAKE_WIDTH < 0 ) //超过边缘
{
leftTop = QPointF(this->width()-SNAKE_WIDTH,snake[0].topLeft().y());
// 窗口宽-蛇宽 左上原y不变
rightBotom = QPointF(this->width(),snake[0].bottomRight().y());
// 窗口宽 右下原y不变
}
else //未超过
{
leftTop = QPointF(snake[0].topLeft().x()-SNAKE_WIDTH,snake[0].topLeft().y());
// 左上x-蛇宽 左上原y不变
rightBotom = QPointF(snake[0].bottomRight().x()-SNAKE_WIDTH,snake[0].bottomRight().y());
// 右下x-蛇宽 右下原y不变
}
snake.insert(0,QRectF(leftTop,rightBotom));
}
void Widget::addRIGHT()
{
QPointF leftTop;
QPointF rightBotom;
if(snake[0].x()+SNAKE_WIDTH*2 > this->width() ) //超过边缘
{
leftTop = QPointF(0,snake[0].topLeft().y());
// 0 左上原y不变
rightBotom = QPointF(SNAKE_WIDTH,snake[0].bottomRight().y());
// 蛇宽 右下原y不变
}
else //未超过边缘
{
leftTop = QPointF(snake[0].topLeft().x()+SNAKE_WIDTH,snake[0].topLeft().y());
// 左上x-蛇宽 左上原y不变
rightBotom = QPointF(snake[0].bottomRight().x()+SNAKE_WIDTH,snake[0].bottomRight().y());
// 右下x-蛇宽 右下原y不变
}
snake.insert(0,QRectF(leftTop,rightBotom));
}
//绘制界面
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QPen pen;//画笔
QBrush brush;//画刷
//加载背景图片
QPixmap pix;
pix.load(":/background/bg_1.jpg");
painter.drawPixmap(0,0,pix.width(),pix.height(),pix);
//绘制蛇
pen.setColor(Qt::black); //设定画笔颜色
brush.setColor(Qt::darkMagenta);//画刷颜色
brush.setStyle(Qt::SolidPattern);//画刷风格
painter.setPen(pen); //画家配置画笔
painter.setBrush(brush);//画家配置画刷
for(int i=0;i<snake.length();i++)
{
painter.drawRect(snake[i]);//画家进行绘画
}
//绘制食物
brush.setColor(Qt::yellow);//画刷颜色
brush.setStyle(Qt::SolidPattern);//画刷风格
painter.setPen(pen); //画家配置画笔
painter.setBrush(brush);//画家配置画刷
painter.drawEllipse(food);
//绘制结束面板
if(isIntersect())
{
QFont font("微软雅黑",30,QFont::ExtraLight,false);
painter.setFont(font);
painter.drawText(this->width()/2-120,
this->height()/2,
QString("GAME OVER!"));
timer->stop();
}
}
//去尾
void Widget::deleLAST()
{
snake.removeLast();
}
// 初始化食物
void Widget::initFood()
{ //QRandomGenerator::global()->generate()随机生成数字
food = QRectF(QRandomGenerator::global()->generate()%(this->width()/20)*20,
QRandomGenerator::global()->generate()%(this->height()/20)*20,
SNAKE_WIDTH,SNAKE_HEIGHT);
}
//是否碰撞
bool Widget::isIntersect()
{
for(int i=1;i<snake.length();i++)
{
if(snake[0] == snake[i])
{
return true;
}
}
return false;
}
//超时函数
void Widget::timeOut()
{
int count =1;
if(snake[0].intersects(food))
{
count++;
//重新初始化食物,即令其刷新位置
initFood();
}
while(count--)
{
switch (moveFlag)
{
case UP:
addTOP();
break;
case DOWN:
addBUTOM();
break;
case LEFT:
addLEFT();
break;
case RIGHT:
addRIGHT();
break;
default:
return;
break;
}
}
deleLAST();
update(); //重新绘制,即更新图像
}
4效果
//重新初始化食物,即令其刷新位置
initFood();
}
while(count–)
{
switch (moveFlag)
{
case UP:
addTOP();
break;
case DOWN:
addBUTOM();
break;
case LEFT:
addLEFT();
break;
case RIGHT:
addRIGHT();
break;
default:
return;
break;
}
}
deleLAST();
update(); //重新绘制,即更新图像
}
## 4效果
![greedy_snake](https://img-blog.csdnimg.cn/img_convert/709a852d84a10ece80eabe1982fbfda0.gif)