【游戏制作】 从零开始的Qt5贪吃蛇代码分析
来源:互联网 发布:java web书籍 编辑:程序博客网 时间:2024/04/30 13:19
悲伤的现实
离期末第一场考试之剩下32天了,而oop的期中project还有24天截止。此刻,我们刚刚写完了人生中第一个c++程序,分数类的运算符重载。继承和多态还没学。尽管如此,也是时候作为全裸勇者勇敢地去挑战大魔王了。
目标
通过研究贪吃蛇小游戏的源码,学会用Qt进行游戏开发,并且完成期中Pro报告中挖下的2D Rougelike类小游戏第一关的大坑,并且写下博客给新鲜的学妹学弟留下可以借鉴的学习经验。
资料
Qt5贪吃蛇小游戏源代码下载处
来自Rimond_Jing的Qt5基本安装教程
来自 齐亮,非常棒的一个参考资料
高能预警
因为目前c++也没学好,Qt也刚开始学,所以本篇博客只适合和我一样什么都不会的人。而且很有可能错误连篇,期待有人能指正。
目前在一遍分析一遍写博客,所以会对内容进行不断地修正
以及非常啰嗦(和废话)
总体分析
注:纯新手一上来就看总体分析极有可能不懂,看看就好,不懂就跳过去看完全文回来再看。
界面转换
在本例子中,采取建立不同的界面类(mainWidget,开始界面,和GameWidget,游戏界面),通过接收事件(event,本例是按下按钮),利用信号和槽,来进行界面切换。
main函数分析
感觉并不需要分析:)
#include "mainWidget.h"//自己写的主窗口的头文件#include <QApplication>//一个基础的类,所有工程(pro文件)都要include。//有时候include这个文件会报错,可能是因为是Qt4的代码的原因int main(int argc, char *argv[]){ QApplication a(argc, argv); mainWidget w; w.show(); return a.exec();}
主窗口分析:mainWidget
头文件分析
#ifndef MAINWIDGET_H#define MAINWIDGET_H#include "GameWidget.h"#include <QWidget>#include <QIcon>#include <QPalette>#include <QBrush>#include <QPixmap>#include <QPushButton>#include <QMessageBox>#include <QLabel>#include <QFont>class mainWidget : public QWidget//继承了QWidget类{ Q_OBJECT//只要有槽和信号机制,就要写Q_OBJECTpublic: mainWidget(QWidget *parent = 0); ~mainWidget(); //void resizeEvent(QResizeEvent *);private: QPushButton *startbtn;//一个按钮,用鼠标点击后会开始游戏 QPushButton *exitbtn;//一个按钮,用鼠标点击后会退出 GameWidget *g; QLabel *label;signals://信号,mainWidget不会发出信号public slots://可以接收所有信号的公共槽 void exitSlot();//用来接收退出信号的槽 void startSlot();//用来接收开始信号的槽};#endif // MAINWIDGET_H
.cpp文件分析
注释的都是可以在自己的程序中使用的函数
#include "mainWidget.h"mainWidget::mainWidget(QWidget *parent): QWidget(parent){ this->resize(480,270);//resize函数,用来设置mainWidget这个窗口的大小 this->setMaximumSize(480,270); this->setWindowIcon(QIcon(":/new/prefix1/img/icon.png"));//设置ICON this->setWindowTitle("贪吃蛇"); QPalette palette; palette.setBrush(QPalette::Background,QBrush(QPixmap(":/new/prefix1/img/back.jpg").scaled(this->size()))); this->setPalette(palette); startbtn=new QPushButton(this); startbtn->setIcon(QIcon(":/new/prefix1/img/start.png")); startbtn->setIconSize(QSize(75,75)); startbtn->setGeometry(QRect(250,170,75,75)); startbtn->setFlat(true); exitbtn=new QPushButton(this); exitbtn->setIcon(QIcon(":/new/prefix1/img/quit.png")); exitbtn->setIconSize(QSize(70,70)); exitbtn->setGeometry(QRect(350,170,70,70)); exitbtn->setFlat(true); //设置说明标签 QFont font; font.setFamily("Consolas"); font.setBold(true); font.setPixelSize(13); label=new QLabel(this); label->setText("游戏说明:贪吃蛇游戏可使用按钮或者w a s d控制蛇的走动"); label->setFont(font); label->setGeometry(QRect(10,10,400,50)); connect(exitbtn,SIGNAL(clicked()),this,SLOT(exitSlot())); connect(startbtn,SIGNAL(clicked()),this,SLOT(startSlot()));}mainWidget::~mainWidget(){ delete startbtn; delete exitbtn;}void mainWidget::exitSlot(){ if(QMessageBox::question(this,"退出游戏","是否退出当前游戏",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes) { delete this; exit(0); }}void mainWidget::startSlot(){ g=new GameWidget(this); g->show();}//重写resizeEvent/*void mainWidget::resizeEvent(QResizeEvent *){ QPalette palette; palette.setBrush(QPalette::Background,QBrush(QPixmap("img/back.jpg").scaled(this->size()))); this->setPalette(palette); startbtn->setGeometry(QRect(this->size().width()-230,this->size().height()-100,75,75)); exitbtn->setGeometry(QRect(this->size().width()-130,this->size().height()-100,70,70));}*/
QMessageBox的初始化及效果
void mainWidget::exitSlot(){ if(QMessageBox::question(this,"退出游戏","是否退出当前游戏",QMessageBox::Yes|QMessageBox::No)==QMessageBox::Yes) { delete this; exit(0);//直接结束进程 } //生成一个弹窗。question的初始化为(链接的窗口,"标题","示意文字",QMessageBox::Yes|QMessageBox::No)}
游戏窗口分析:GameWidgit
游戏窗口是这个程序的主体部分,各种复杂的事件(槽和信号机制),状态的更新,动画、碰撞检测都是在这个类(游戏窗口就是一个继承了普通窗口< Widget >的派生类)中完成的。
头文件分析
#ifndef GAMEWIDGET_H#define GAMEWIDGET_H#include <QWidget>//既然继承自QWidget,肯定需要include相关的头文件#include <QPalette>#include <QIcon>//这些是为了让你在显示背景图片和窗口图标,一般做游戏(自己设计UI)都要用#include <QPushButton>//这个库在你有鼠标事件的事后非常好用#include <QPainter>//如果有动画,需要包含QPainter库(如果只是显示图片可以用其他方法,但游戏一般都有动画)#include <QDebug>//主要用于调试代码,类似于std::cout的替代品,支持QT的数据类型。#include <QTime>#include <QTimer>//和计时有关的库,做游戏也一般都要用到#include <QMessageBox>//如果你的游戏有弹出框,就要包含(非必须)#include <QKeyEvent>//如果你的游戏可以用键盘操纵,需要包含#include <QLabel>//可以用来显示字(也可以显示图片)#include <QFont>//如果你要用Qt绘制字(而不是用自己设计的UI)时,需要包含#include <QSound>//音频相关,游戏一般必须class GameWidget : public QWidget{ Q_OBJECTpublic: //构造函数 explicit GameWidget(QWidget *parent = 0); ~GameWidget(); //绘制事件,游戏中非常重要的函数 void paintEvent(QPaintEvent *); //键盘事件 void keyPressEvent(QKeyEvent *);private: //按钮相当于鼠标事件 QPushButton *upbtn; QPushButton *leftbtn; QPushButton *downbtn; QPushButton *rightbtn; QPushButton *startbtn; QPushButton *returnbtn; //记录蛇目前运动方向 int direction; //用来记录蛇xy坐标,可以看出最大能得100分 int snake[100][2]; int snake1[100][2]; //计算吃过了几个食物 int foodcount; //计时器!和动画的帧数也有关 QTimer *timer; //食物的属性 int foodx,foody; int score; int level; QLabel *scorelabel; QLabel *levellabel; QLabel *scoreshow; QLabel *levelshow; QString str1,str2; QSound *sound; QSound *sound1;signals://因为有键盘事件(KeyPressEvent),键盘事件要发射不同的信号 void UpSignal(); void DownSignal(); void LeftSignal(); void RightSignal();public slots: //一般来说有几个按钮就要有几个槽函数 void upbtnSlot(); void leftbtnSlot(); void rightbtnSlot(); void downbtnSlot(); void startbtnSlot(); void returnbtnSlot(); //本例的主函数,和timer结合使用(比较复杂,之后会详细讲) void timeoutSlot();};#endif // GAMEWIDGET_H
QDebug的说明
.c文件分析
因为GameWidget略长,且有一些部分和MainWidget用法一致,就不放上来全部代码(想要自己运行的可以从之前附上的链接下载,记得只适用于QT5)
注: 需要include相应的库文件
1.如何让你的游戏发出声音
首先要载入音频
//声音区:载入音频,注意音频文件要加入.qrc文件中 sound=new QSound(":/listen/img/5611.wav"); sound1=new QSound(":/listen/img/die.wav");
在什么时候播放声音,使用if语句判断,一般是一个可以update()的槽函数中(本例为timeoutSlot(),见下文)
sound->play();
2.如何即时显示得分
str1=QString::number(score);//转化为字符串 scoreshow=new QLabel(this);//用Label输出 scoreshow->setFont(font); scoreshow->setGeometry(QRect(385,1,60,30)); scoreshow->setText(str1);
3.如何设置交互(信号和槽机制)
//设置按钮操作 connect(leftbtn,SIGNAL(clicked()),this,SLOT(leftbtnSlot())); connect(rightbtn,SIGNAL(clicked()),this,SLOT(rightbtnSlot())); connect(upbtn,SIGNAL(clicked()),this,SLOT(upbtnSlot())); connect(downbtn,SIGNAL(clicked()),this,SLOT(downbtnSlot())); connect(startbtn,SIGNAL(clicked()),this,SLOT(startbtnSlot())); connect(returnbtn,SIGNAL(clicked()),this,SLOT(returnbtnSlot())); //设置键盘操作 connect(this,SIGNAL(UpSignal()),upbtn,SLOT(click())); connect(this,SIGNAL(DownSignal()),downbtn,SLOT(click())); connect(this,SIGNAL(LeftSignal()),leftbtn,SLOT(click())); connect(this,SIGNAL(RightSignal()),rightbtn,SLOT(click()));
connect的使用
connect(发射信号的类,SIGNAL(信号函数),接收信号的类,SLOT(槽函数));
按钮操作的实现
QPushButton这个类中有click()的信号函数和槽函数,如果发生了鼠标点击事件,被点击的Button就发射click()信号,因为我们用connect将这个信号和GameWidget中的槽函数(比如leftbtnSlot())连接起来,GameWidget就能接收到这个信号并且调用相应的槽函数。
键盘操作同理,因为KeyPressEvent是我们自己写在GameWidget类中,所以为 ‘this’ 指针。这次connect是把键盘事件和按钮联系了起来。
4.如何设置随机数
QTime t; t= QTime::currentTime(); qsrand(t.msec()+t.second()*1000);
5.如何update程序
timer=new QTimer(this); timer->setInterval(500);//设置时间间隔 connect(timer,SIGNAL(timeout()),this,SLOT(timeoutSlot()));
该程序设置了一个timer(一个定时触发器,参考这里),并且设置了时间间隔,每过一次时间间隔发送一个timeout()信号。
而GameWidget类中定义了timeoutSlot()槽函数,每过一定时间间隔被调用,执行update()操作,来更新界面状态,下文会讲
6.(重点)timeoutSlot分析
因为这一段很长,也只写出代码结构(需要完整代码见开头链接)
void GameWidget::timeoutSlot(){ //判断是否吃到食物的代码块(判断蛇的坐标和食物的坐标是否重合) //其中包含,发出声音、更新食物坐标(随机更新,食物不能出现在蛇身上)、如果分数到达一定的档次,就提升游戏等级(改变时间间隔) memcpy(snake1,snake,sizeof(snake));//不能直接对目前显示的数组进行操作,因此一开始就定义了两个数组存放当前状态和改变后的状态,用memcpy拷贝 //实现蛇的游动 for(int i=foodcount;i>=1;i--) { snake[i][0]=snake[i-1][0]; snake[i][1]=snake[i-1][1]; } switch(direction) { case UP:snake[0][1]--;break; case DOWN:snake[0][1]++;break; case LEFT:snake[0][0]--;break; case RIGHT:snake[0][0]++;break; } //判断蛇是否撞到自身的代码块,如果撞到,显示游戏结束的弹窗,并重置游戏 //判断蛇是否撞到墙体的代码块 //最关键,调用update()函数,更新窗口状态 this->update();}
memcpy辅助理解
update辅助理解
其中的碰撞检测机制是通过判断xy坐标是否相同来进行的。
Qt工具书上似乎有其他碰撞检测的办法,挖个坑
7.(重点)paintEvent分析
函数声明
void GameWidget::paintEvent(QPaintEvent *);
要定义一个绘制器painter
QPainter painter(this);
画一个小方格(长方形)的代码(该游戏画了很多很多小方格),只摘取一个:j,i是坐标,20, 20是方格的大小
painter.drawRect(j,i,20,20);
读取素材绘制
foodx要乘20的原因是这是相对坐标,而不是绝对坐标
painter.drawImage(foodx*20,foody*20,QImage(":/new/prefix1/img/apple.png").scaled(QSize(20,20)));
该游戏主要用到的就是这两种绘制方式、当然还有更多的绘制方式:参考这里
今日学习获得的一些经验
- Qt4和Qt5虽然用法差别不大,但是Qt4的代码在Qt5上很难跑通。Qt4的游戏源代码值得参考,但要自己尝试运行,还是去找Qt5的源代码吧。
- Qt的各种参考书和博客中给的代码多是模板,不要直接放在Qt中运行
- Qt中读取资源(图片、音频etc)都要把文件加入qrc文件中以获取相对路径
吐槽:根本不是今日获得的经验,从上周开始写一直拖到现在orz
end
- 【游戏制作】 从零开始的Qt5贪吃蛇代码分析
- Qt C++制作贪吃蛇游戏(在Qt5.4.2可以正常运行)
- 贪吃蛇游戏代码
- 贪吃蛇游戏代码
- 贪吃蛇游戏代码
- 贪吃蛇游戏代码
- 贪吃蛇游戏代码
- 贪吃蛇游戏代码
- 贪吃蛇游戏代码
- 零基础HTML5游戏制作教程 第6章 贪吃蛇的实现及代码
- Android制作贪吃蛇游戏
- Qt制作贪吃蛇游戏
- 简单的贪吃蛇游戏 代码
- QT5写的贪吃蛇
- 字符游戏——简单贪吃蛇的制作
- 贪吃蛇的制作
- 贪吃蛇游戏代码翻译
- js贪吃蛇游戏代码
- 关于spring+hibernate中的OpenSession()和getCurrentSession()的区别
- java 多态简述
- 树莓派拍照脚本
- OpenCL和CUDA关系
- gedit——编程利器
- 【游戏制作】 从零开始的Qt5贪吃蛇代码分析
- chmod
- LiberoSOC分配管脚——Tcl脚本编写
- Zookeeper简介和入门安装示例
- 写xml文件的工具
- cordova + ionic前端框架 js和android ios原生(native)交互
- 软件工程-项目总结——项目架构
- 毕业设计笔记--Ubuntu 14.0.4下使用Android Studio编译Mapbox(为以后节省大量时间)
- 【OI做题记录】【BZOJ】【HAOI2006】旅行comf