贪吃蛇的需求分析
1.通过方向键控制蛇头进行左右上下移动
2.随机生成食物供贪吃蛇吃,吃到食物蛇身变长
3.蛇头碰到蛇身或边界结束游戏
具体实现
控制蛇上下移动:使用QTimer类定义一个定时器,该定时器不断地发出timeout信号,相应的槽函数就会不断地接收该信号来执行动作以控制蛇移动,然后不断地进行重新渲染,以达到蛇不断移动的效果。前面说过蛇的移动实际上是在头部增加一个小方块,在尾部删除一个小方块,因此可以封装4个成员函数分别控制蛇在4个方向的头部增加,同时再封装一个成员函数表示尾部的删除。
食物生成:食物可以用一个小方块来表示,然后封装一个成员函数来随机分配食物这个小方块的位置,先在构造函数中对其进行初始化,最后通过渲染函数将其画在界面中。
蛇身变长:只需判断蛇头小方块是否和食物小方块重合,一旦重合,那我们在头部多增加一个小方块,就表示蛇增长了。比如蛇朝右移动,我们在头部增加一个小方块,尾部删除一个小方块,表示蛇的移动,那现在,我们在头部增加两个小方块,在尾部删除一个小方块,就表示蛇增长了。
结束游戏:蛇头坐标与蛇身或边界重合,判定游戏结束。
源代码如下:
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("贪吃蛇");
timer = new QTimer();
//定时器关联
connect(timer,SIGNAL(timeout()),this,SLOT(timeout()));
resize(1200,500);
//初始化蛇身
QRectF rect(500,230,nodeWidht,nodeHeight);
snake.append(rect);
addTop();
addTop();
addNewReward();//初始化食物
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event){
switch (event->key()) {//方向冲突时的应对措施
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(100);//定时器启动
}
else {
gamestart=false;
timer ->stop();
}
break;
default:
break;
}
}
void Widget::timeout(){
if(checkContact())return;
//吃到食物
int counter = 1;
if(snake[0].intersects(rewardNode)){
counter++;
score++;
addNewReward();
}
while(counter--){
switch(moveFlag){
case DIR_UP://顶部加方块
addTop();
break;
case DIR_DOWN:
addDown();
break;
case DIR_LEFT:
addLeft();
break;
case DIR_RIGHT:
addRight();
break;
}
}
deleteLast();
update();//自动调用重绘方法
}
void Widget::addTop(){//向上
QPointF leftTop;
QPointF rightBottom;
if(snake[0].y() - nodeHeight < 0){
leftTop = QPoint(snake[0].x(),this->height() - nodeHeight);
rightBottom = QPointF(snake[0].x() + nodeWidht,this->height());
}
else{
leftTop = QPointF(snake[0].x(),snake[0].y()-nodeHeight);//左下角坐标
rightBottom = snake[0].topRight();//右下角坐标
}
snake.insert(0,QRectF(leftTop,rightBottom));
}
void Widget::paintEvent(QPaintEvent *event){
//画游戏界面
QPainter painter(this);
QPen pen;
QBrush brush;
//背景图
QPixmap pix;
pix.load("D:/Qtcode/snake.jpg");
painter.drawPixmap(0,0,1200,500,pix);
//画蛇
pen.setColor(Qt::yellow);
brush.setColor(Qt::blue);
brush.setStyle(Qt::SolidPattern);
painter.setPen(pen);
painter.setBrush(brush);
for(int i=0;i<snake.length();i++){
painter.drawRect(snake[i]);
}
//画食物
pen.setColor(Qt::green);
brush.setColor(Qt::green);
brush.setStyle(Qt::SolidPattern);
painter.setPen(pen);
painter.setBrush(brush);
painter.drawEllipse(rewardNode);
//结束画面
if(checkContact()){
QFont font("宋体",30,QFont::ExtraLight,false);
painter.setFont((font));
painter.drawText((this->width ()-300)/2,
(this->height()-30)/2,
QString("游戏结束"));
timer->stop();
}
//画分数记录
pen.setColor(Qt::red);
brush.setColor(Qt::red);
painter.setPen(pen);
painter.setBrush(brush);
QFont font("宋体",20,QFont::ExtraBold,false);
painter.setFont(font);
painter.drawText(0,nodeHeight*3,
QString("分数:"+QString::number(score*10)));
}
void Widget::deleteLast(){
snake.removeLast();
}
void Widget::addDown(){//向下
QPointF leftTop;
QPointF rightBottom;
//向下时如果接触到底端,则从对应的顶端重新生成蛇头
if(snake[0].y() + nodeHeight * 2 > this->height()){
leftTop = QPointF(snake[0].x(),0);
rightBottom = QPointF(snake[0].x() + nodeWidht,nodeHeight);
}
//没有触碰下边界时,正常加减节点
else{
leftTop = snake[0].bottomLeft();
rightBottom = snake[0].bottomRight() + QPointF(0,nodeHeight);
}
snake.insert(0,QRectF(leftTop,rightBottom));
}
void Widget::addLeft(){//向左
QPointF leftTop;
QPointF rightBottom;
if(snake[0].x() - nodeWidht<0){
leftTop = QPointF(this->width() - nodeWidht,snake[0].y());
}
else{
leftTop = snake[0].topLeft() - QPointF(nodeWidht,0);
}
rightBottom = leftTop + QPointF(nodeWidht,nodeHeight);
snake.insert(0,QRectF(leftTop,rightBottom));
}
void Widget::addRight(){//向右
QPointF leftTop;
QPointF rightBottom;
if(snake[0].x() + nodeWidht*2 > this->width()){
leftTop = QPointF(0,snake[0].y());
}
else{
leftTop = snake[0].topRight();
}
rightBottom = leftTop + QPointF(nodeWidht,nodeHeight);
snake.insert(0,QRectF(leftTop,rightBottom));
}
void Widget::addNewReward(){//随机生成食物
rewardNode = QRectF(
qrand()%(this->width()/20)*20,
qrand()%(this->height()/20)*20,
nodeWidht,
nodeHeight);
}
bool Widget::checkContact(){//身体和头接触结束
for(int i=1;i<snake.length();i++){
if(snake[0] == snake[i]){
return true;
}
}
return false;
}