赞
踩
提示:本专栏所用版本仅供参考,其他版本也可
库 | 版本 |
---|---|
QT | 5.14:下载 |
源码下载 | 点击下载 |
游戏包下载 | 点击下载 |
QQ群 | 点击加群:928357277 |
本教程将讲述如下内容
1:如何在画布上绘制贪吃蛇的身体
2:如何生成豆子
3:如何让贪吃蛇被键盘控制
4:如何让贪吃蛇在固定画布内移动
5:键盘事件,定时器事件,画布事件的学习
关于本教程所需的源码及其EXE文件将打包上传,欢迎大家下载,也可以加群免费下载。
我们首先在类的protected成员中创建一个QT类中的画布事件,在事件函数内实现相关功能
//函数在类内申明 void paintEvent(QPaintEvent *event);//定义画笔事件 { QPainter painter(this);//创建一张画布 QPen pen;//创建一只画笔对pen painter.setPen(pen);//将画笔设置到画布中去 QBrush brs(Qt::black);//创建并设置笔刷为黑色 QFont font("黑体",12,QFont::ExtraLight,false);//说明栏设置 painter.setFont(font);//画布插入字体 painter.drawText(20,40,QString("控制:")+QString("移动:上下左右——出豆子:空格——退出:ESC")); }
定义一个长10宽10,且初始长度为4(也就是4个10*1长度的小蛇),他的每个身体方块的坐标存入QLIST容器中
#define itsBoy 10//定义蛇的身体大小为
#define mapMin 40 //地图X坐标的最远距离
#define mapMax 490 //地图Y坐标的最远距离
private:
int snakeInit = 4;//定义初始化长度
QList<int>snake_x;//用LIST保存蛇的身体
QList<int>snake_y;
int snake_boy=itsBoy; //蛇的身体大小
在画布中画出蛇的第一个身体
void paintEvent(QPaintEvent *event);//定义画笔事件 { QPainter painter(this);//定义一张画布 QPen pen;//定义一张画笔 painter.setPen(pen);//将画笔设置到画布中去 pen.setWidth(snake_boy);//笔刷宽度设置为蛇的宽度 QBrush brs(Qt::black);//设置笔刷为黑色 QFont font("黑体",12,QFont::ExtraLight,false);//计数栏设置 painter.setFont(font);//画布插入字体 painter.drawText(20,40,QString("控制:")+QString("移动:上下左右——出豆子:空格——退出:ESC")); brs.setColor(Qt::darkGray);//改变笔刷颜色 painter.setBrush(brs); //画出矩形,蛇 for(int i=0;i<snake_x.length();i++) { painter.setBrush(brs);//设置画笔笔刷 painter.drawEllipse(snake_x.at(i),snake_y.at(i),snake_boy,snake_boy);//画出蛇的身体 } }
创建一个大小等于蛇身体方块大小的方块作为豆子
private:
int snakeBean=itsBoy;//豆子的身体
int beanx=-100;//豆子的坐标
int beany=-100;
在画布中绘制豆子
void mainUi::paintEvent(QPaintEvent *event) //画家函数 { QPainter painter(this);//定义一张画布 QPen pen;//定义一张画笔 painter.setPen(pen);//将画笔设置到画布中去 pen.setWidth(snake_boy);//笔刷宽度设置为蛇的宽度 QBrush brs(Qt::black);//设置笔刷为黑色 QFont font("黑体",12,QFont::ExtraLight,false);//计数栏设置 painter.setFont(font);//画布插入字体 painter.drawText(20,40,QString("控制:")+QString("移动:上下左右——出豆子:空格——退出:ESC")); brs.setColor(Qt::darkGray);//改变笔刷颜色 painter.setBrush(brs); //画出矩形,蛇 for(int i=0;i<snake_x.length();i++) { painter.setBrush(brs);//设置画笔笔刷 painter.drawEllipse(snake_x.at(i),snake_y.at(i),snake_boy,snake_boy);//画出蛇的身体 } //画豆子 if(beanx>0&&beany>0) { brs.setColor(Qt::darkMagenta);//设置豆子颜色 brs.setStyle(Qt::ConicalGradientPattern);//设置笔刷样式 painter.setBrush(brs); painter.drawRect(beanx,beany,snake_boy,snake_boy);//画出豆子的身体 } }
需要注意的是,当小蛇头部朝下的时候,按照游戏的逻辑,不能直接控制小蛇朝上移动,所以,在写键盘事件的时候,我们应该判断小蛇的朝向问题
void mainUi::keyPressEvent(QKeyEvent *event) //定义键盘事件 { switch(event->key()) { case Qt::Key_Up://枚举值为键盘上的向上方向键 if(moveFlag!= 'D')//如果小蛇头没有朝下,则可以直接朝上移动,否则,小蛇不产生任何变化 { moveFlag ='U'; } break; case Qt::Key_Down: if(moveFlag !='U') { moveFlag ='D'; } break; case Qt::Key_Right: if(moveFlag != 'L') { moveFlag ='R'; } break; case Qt::Key_Left: if(moveFlag != 'R') { moveFlag='L'; } break; case Qt::Key_Space://按下空格键 getaBean();//生成豆子,大家也可以不写按下空格键,直接把该函数写在吃豆子的函数内,这样写是为了方便演示 break; case Qt::Key_Escape://按下退出键,退出游戏 this->close(); } update(); }
前面,我们已经在画布上绘制了一条小蛇,并且在键盘事件中写入了小蛇移动的条件。那么小蛇具体应该怎么移动呢?
写之前我们要明白如下原理
1、小蛇的移动首先应该判断他当前移动的方向,如左边,右边,我们用一个变量来保存这个这个方向。
2、小蛇在移动期间,除了头部,也就是第一个坐标需要判断下一个要移动的坐标,其他蛇身只需要将坐标移信息改变为前一个身体的信息即可。即第三个身体在向前移动后,他的坐标信息将变成第二个身体的坐标信息,不需要重新计算。
3、小蛇要在没有按下按键的清空下移动,就需要有一个一直更新画布的函数,在QT中,定时器可以完成这个动作。
5,、小蛇移动过过程中,判断窗口边界和吃豆子,只需要用头部所处的位置判断即可,因为后续所有身体的移动,都要经过头部。
//除了第一个,之后每次的坐标都和前一次坐标相等 void mainUi::moveUp()//要注意移动的时候的坐标 { int snakexbrfore[2]={0},snakeybrfore[2]={0},signale=0; if((snake_y[0]>snake_y[1]&&snake_x[0]==snake_x[1])||snake_y[0]<=mapMin){signale=1;}//判断边界,并且判断第二个身体在第一个身体的哪个位置 for(int i=0;i<snake_y.length()&&signale==0;i++)//如果没有就吃豆子 { if(i==0&&snake_y[i]<=snake_y[i+1]){ snakexbrfore[0]=snake_x[i];snakeybrfore[0]=snake_y[i]; snake_y[i]-=snake_boy; if(snake_x[i]==beanx&&snake_y[i]==beany){ snake_x.append(beanx);snake_y.append(beany); beanx=-mapMax;beany=-mapMax; } continue; } movedll(snakexbrfore,snakeybrfore,i);//除头部以外的身体移动函数 } } //向下移动 void mainUi::moveDown() { int snakexbrfore[2]={0},snakeybrfore[2]={0},signale=0; if((snake_y[0]<snake_y[1]&&snake_x[0]==snake_x[1])||snake_y[0]>=mapMax){signale=1;}//判断边界,并且判断第二个身体在第一个身体的哪个位置 for(int i=0;i<snake_y.length()&&signale==0;i++) { if(i==0){ snakexbrfore[0]=snake_x[i];snakeybrfore[0]=snake_y[i]; snake_y[i]+=snake_boy; if(snake_x[i]==beanx&&snake_y[i]==beany){ snake_x.append(beanx);snake_y.append(beany); beanx=-mapMax;beany=-mapMax; } continue; } movedll(snakexbrfore,snakeybrfore,i);//除头部以外的身体移动函数 } } /* 遍历整个列表:同X坐标判断,每次只移动一次 */ void mainUi::moveLeft()//向左移动 { int snakexbrfore[2]={0},snakeybrfore[2]={0},signale=0; if((snake_x[0]>snake_x[1]&&snake_y[0]==snake_y[1])||snake_x[0]<=mapMin){signale=1;} for(int i=0;i<snake_y.length()&&signale==0;i++) { if(i==0){ snakexbrfore[0]=snake_x[i];snakeybrfore[0]=snake_y[i]; snake_x[i]-=snake_boy; if(snake_x[i]==beanx&&snake_y[i]==beany){ snake_x.append(beanx);snake_y.append(beany); beanx=-mapMax;beany=-mapMax; } continue; } movedll(snakexbrfore,snakeybrfore,i); } } void mainUi::moveRight()//向右移动 { int snakexbrfore[2]={0},snakeybrfore[2]={0},signale=0; if((snake_x[0]<snake_x[1]&&snake_y[0]==snake_y[1])||snake_x[0]>=mapMax){signale=1;} for(int i=0;i<snake_y.length()&&signale==0;i++) { if(i==0){ snakexbrfore[0]=snake_x[i];snakeybrfore[0]=snake_y[i]; snake_x[i]+=snake_boy; if(snake_x[i]==beanx&&snake_y[i]==beany){ snake_x.append(beanx);snake_y.append(beany); beanx=-mapMax;beany=-mapMax; } continue; } movedll(snakexbrfore,snakeybrfore,i); } } void mainUi::movedll(int *x,int *y,int i)//公共函数部分//除头部以外的身体移动 { *(x+1)=snake_x[i];*(y+1)=snake_y[i]; snake_x[i]=*x;snake_y[i]=*y; *x=*(x+1);*y=*(y+1); } //定时器中断执行函数 void mainUi::timefoult() { current_time++; if(moveFlag == 'D') { moveDown(); } if(moveFlag == 'U') { moveUp(); } if(moveFlag == 'L') { moveLeft(); } if(moveFlag == 'R') { moveRight(); } update();//更新画布 }
类中的相关构造和定义
#define mapMin 40 //地图X坐标的最远距离
#define mapMax 490 //地图Y坐标的最远距离
private:
char moveFlag ='u'; //定义初始化方向
QTimer *timer = nullptr;//创建定时器对象
protected:
void keyPressEvent(QKeyEvent *event);//定义键盘事件
void paintEvent(QPaintEvent *event);//定义画笔事件
用C++提供的随机数生成器Srand()生成豆子的坐标
void mainUi::getaBean()//生成一个豆子,也就是随机生成一个整数坐标(位于500之内)
{
//用四舍五入法生成坐标
int x=40+(rand()%mapMax-40),y=rand()%mapMax;//X,Y的范围为40 ~ mapMax之间
if((x%10)!=0){x+=(10-(x%10));}
if((y%10)!=0){y+=(10-(y%10));}
beanx=x,beany=y;//坐标保存至类中的beanx,beany变量中
}
mainUi::mainUi(QWidget *parent) : QWidget(parent) { // ui->setupUi(this); this->setWindowTitle("行走的小蜜蜂"); this->setWindowIcon(QIcon(":/photo/green.jpg"));//加入图标 resize(mapMax+10,mapMax+10);//窗口尺寸 X ,Y setStyleSheet("QWidget{background:white}"); //设置窗口颜色 setWindowFlags(Qt::FramelessWindowHint);//去掉标题栏 this->setAttribute(Qt::WA_TranslucentBackground);//设置背景透明 srand((unsigned)time(NULL));//把时间作为随机数种子 timer = new QTimer;//创建一个定时器对象 timer->start(100);//开启定时器100ms connect(timer,&QTimer::timeout,this,&mainUi::timefoult);//定时器信号 snake_x<<100<<100<<100<<100;//定义初始化坐标位置 snake_y<<100<<110<<120<<130; }
//头文件中包含了其他项目的头文件,大家根据需要删除即可 #include "QKeyEvent" #include <QWidget> #include <QRectF> #include <QPainter> #include <QPen> #include <QBrush> #include <QDebug> #include <QTimer> #include <QDebug> #include <QPaintEvent> #include <QTimerEvent> #include <QIcon> #include <QPushButton> #include <QVBoxLayout> #include <QHBoxLayout> #include <QPalette> #define itsBoy 10 #define mapMin 40 #define mapMax 490 namespace Ui { class mainUi; } class mainUi : public QWidget { Q_OBJECT public: explicit mainUi(QWidget *parent = 0); ~mainUi(); private: /******************************** 移动 ********************************/ void moveLeft(); void moveRight(); void moveUp(); void moveDown(); void movedll(int *x,int *y,int i);//公共函数部分用一个函数代替 /******************************** 豆子 ********************************/ void getaBean(); void timefoult(); private: Ui::mainUi *ui; char moveFlag ='u'; //定义初始化方向 int snakeInit = 4;//定义初始化长度 QTimer *timer = nullptr; QList<int>snake_x;//用LIST保存蛇的身体 QList<int>snake_y; int snake_boy=itsBoy; int current_time=0; int snakeBean=itsBoy; int beanx=-100;//豆子的坐标 int beany=-100; protected: void keyPressEvent(QKeyEvent *event);//定义键盘事件 void paintEvent(QPaintEvent *event);//定义画笔事件 };
运行结果:(屏幕中间区域为游戏运行区域)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。