真是应了那句“温故而知新”,尤其是接触一个新东西不久,每每再回过头来看总会发现一些新东西,记得刚开始接触QT时,感觉它的结构真是不人性化,自己写个C++类还要要注册,继承,还有在构造函数下写东西,总感觉有点乱七八糟的。做为一个不入门的小白,开始的时候妄图直接把自己的纯C++代码粘贴过来运行(粘到了mywidget.cpp中,就是一开始自己定义的那个名字生成的cpp文件中),可是自己在VS下写的类和枚举类拿过来竟然都不能用了。哇。。。本来是给C++当UI用的,结构看起来有点复杂就罢了,C++的东西都不通用吗,网上博客上各种找答案,都没有解决办法,最后试了下用struct竟然是可以的,索性先都用struct来代替class,直到后来心血来潮几次回来重新看自己写的‘贪吃蛇’小游戏,才发现当初写的结构是乱七八糟的(当然水平有限,也只是稍微改了下),还有终于发现了怎么把纯C++定义的类直接复制过来使用了,竟如此简单。。。
上图理了一下头文件和cpp文件的相互包含关系,凡是自己新添加的头文件(包括自己的类,这里可以是纯C++类)都要被包含在默认生成的头文件中,如上图就是mywidget.h中,还有一些全局定义的宏和各种需要包含的库文件一般都包含在这个“总”的头文件里面,而这个头文件会自动被包含在默认生成的main.cpp和mywidget.cpp中,总之要想自己写的类直接拿过来用,再新建头文件,在里面定义就可以了,而直接在mywidget.cpp中写的话,就需要继承注册什么的了(小白表示暂时不懂,也不想懂),还是这样干净明了一些,毕竟class还是看上去比struct要高级一点。。
下面是所有文件的代码,水平一般,但是总归比之前要看上去不那么乱糟糟一点。。
#ifndef POINT
#define POINT
#include "iostream"
using namespace std;
class Point {
int xvar;
int yvar;
public:
Point() {}
~Point() {}
Point(int x, int y) :xvar(x), yvar(y) {}
int x()const { return xvar; }
int y()const { return yvar; }
};
#endif // POINT
#ifndef SNAKE
#define SNAKE
#include "iostream"
using namespace std;
enum Sdirections{s_up,s_down,s_left,s_right};
class Snake {//蛇类
Sdirections s_dire;
bool s_alive;
public:
Snake() {}
~Snake() {}
Snake(Sdirections dire,bool alive){
s_dire = dire;
s_alive = alive;
}
Sdirections getSdirect(){
return s_dire;
}
void setSdirect(Sdirections d){
s_dire = d;
}
bool isAlive(){
return s_alive;
}
void Salive(bool b){
s_alive = b;
}
std::vector snakeBody;//蛇身
};
#endif // SNAKE
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //键盘事件
#include
#include
#include "point.h"
#include "snake.h"
//全局宏定义
#define Bsize 20//格子的尺寸(像素)
#define startX 50//地图起始x坐标
#define startY 75//地图起始y坐标
#define Cwidth 500//地图宽500px
#define Cheight 420//地图高420px
#define W 25//宽25格
#define H 21//高21格
#define appleW 19//苹果宽19px
#define appleH 22//苹果高22px
#define snakeLen 4//初始蛇长度
#define snakeSpeed 1//蛇速,待定
namespace Ui {
class myWidget;
class Point;
}
class myWidget : public QWidget
{
Q_OBJECT
public:
explicit myWidget(QWidget *parent = 0);
~myWidget();
protected:
//重写绘图事件,虚函数
//如果在窗口绘图,必须在绘图事件里实现
//绘图事件内部自动调用,窗口需要重绘时,即状态改变时
void paintEvent(QPaintEvent *);
void keyPressEvent(QKeyEvent *);
void timerEvent(QTimerEvent *);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_4_clicked();
void on_pushButton_5_clicked();
private:
Ui::myWidget *ui;
int timerId;//建立时钟的ID
};
#endif // MYWIDGET_H
#include "mywidget.h"
#include "ui_mywidget.h"
//全局变量声明
int appleX, appleY;//苹果格子坐标
int posAppleX, posAppleY;//苹果像素坐标
int headX, headY;//蛇头坐标
int score = 0;
QString strScore;
bool isEated = false;//是否吃到了?
bool crashed = false;//是否撞墙或者撞到自己
bool gamePause = false;//游戏是否暂停
bool bg = true;
bool bgm = true;
bool gameStart = false;//需要点击开始才可以玩耍
int Smap[W][H];//地图尺寸,值为信息标记
int Sround = 1;//第一关
int gameStartClickCount = 0;
QMediaPlayer *player = new QMediaPlayer;
QMediaPlayer *player_gg = new QMediaPlayer;
QMediaPlayer *player_eat = new QMediaPlayer;
//这里写全局函数声明
void init();
void createApple();
void goUp();
void goDown();
void goLeft();
void goRight();
void autoRun();
void setRound();//设置关卡
void snakeInit();//初始化蛇身
Snake s(s_right,!crashed);//建立蛇对象并初始化
myWidget::myWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::myWidget)
{
ui->setupUi(this);
setWindowTitle("贪吃蛇小游戏");
setWindowIcon(QIcon("../image/apple1.png"));//设置窗口图标
init();
qDebug() << s.snakeBody.size();
timerId = this->startTimer(500);//毫秒为单位,一个叫做timerId的定时器
connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
player->setMedia(QUrl::fromLocalFile("../image/bgm2.mp3"));//游戏背景音乐
player->setVolume(30);
player->play();
connect(player_gg, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
player_gg->setMedia(QUrl::fromLocalFile("../image/gg.mp3"));//GameOver的声音
player_gg->setVolume(80);
connect(player_eat, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
player_eat->setMedia(QUrl::fromLocalFile("../image/eated.mp3"));//吃到东西的声音
player_eat->setVolume(80);
}
myWidget::~myWidget()
{
delete ui;
}
void myWidget::timerEvent(QTimerEvent *e){//定时器
if(e->timerId() == timerId && gamePause){
if(s.getSdirect() == s_right){
goRight();
}else if(s.getSdirect() == s_up){
goUp();
}else if(s.getSdirect() == s_down){
goDown();
}else if(s.getSdirect() == s_left){
goLeft();
}
//qDebug()<label->setText(strScore);
}
update();
if(crashed){
player->stop();
player_gg->play();
Sleep(1000);
MessageBox(0, TEXT("不要灰心!继续游戏??"), TEXT("Sorry,defeat!!"), 0);//游戏结束时
gamePause = !gamePause;
init();
strScore = QString::number(score);
ui->label->setText(strScore);
}
}
}
void myWidget::keyPressEvent(QKeyEvent *e){//键盘事件监听
// qDebug()<<(char)e->key();
if(gameStart){//游戏开始时才可以手动
if(e->key() == Qt::Key_W){
if(s.getSdirect() == s_down) return;//不做处理
s.setSdirect(s_up);
//if(e->isAutoRepeat())//重复按下再快跑会有迟钝效果。。
goUp();
if(isEated)
createApple();//随机产生苹果位置
}else if(e->key() == Qt::Key_S){
if(s.getSdirect() == s_up) return;//不做处理
s.setSdirect(s_down);
goDown();
if(isEated)
createApple();//随机产生苹果位置
}else if(e->key() == Qt::Key_A){
if(s.getSdirect() == s_right) return;//不做处理
s.setSdirect(s_left);
goLeft();
if(isEated)
createApple();//随机产生苹果位置
}else if(e->key() == Qt::Key_D){
if(s.getSdirect() == s_left) return;//不做处理
s.setSdirect(s_right);
goRight();
if(isEated)
createApple();//随机产生苹果位置
}
strScore = QString::number(score);
ui->label->setText(strScore);
update();
}
}
void myWidget::paintEvent(QPaintEvent *)
{
//QPainter p(this);
QPainter p;//创建画家对象
p.begin(this);//指定当前窗口为绘图设备
//绘图操作
//p.drawPixmap(0,0,width(),height(),QPixmap("../image/bg.jpg"));//背景图
if(bg)
p.drawPixmap(rect(),QPixmap("../image/bg.jpg"));//背景图
else{
p.drawPixmap(rect(),QPixmap("../image/bg.png"));//背景图
}
//定义画笔
QPen pen;
pen.setWidth(6);
pen.setColor(RGB(60,60,60));
p.setPen(pen);//把画笔交给画家
// p.drawRect(startX,startY,Cwidth,Cheight);//画出游戏边界
for(int i=-1;i<=W;i++){//画出游戏边界
p.drawPixmap(i*Bsize+startX,-1*Bsize+startY,Bsize,Bsize,QPixmap("../image/shitou.png"));
p.drawPixmap(i*Bsize+startX,21*Bsize+startY,Bsize,Bsize,QPixmap("../image/shitou.png"));
}
for(int j=0;jlabel->setText(strScore);
if(appleX%2==0)//画苹果
p.drawPixmap(posAppleX,posAppleY-2,appleW,appleH,QPixmap("../image/apple1.png"));
else//画月饼
p.drawPixmap(posAppleX,posAppleY,Bsize,Bsize,QPixmap("../image/shead.png"));
if(isEated){
createApple();//随机产生苹果位置
update();
}
for(std::vector::iterator it=s.snakeBody.begin();it!=s.snakeBody.end();it++){//遍历蛇身,画蛇
Smap[(*it).x()][(*it).y()] = 1;
if(it == s.snakeBody.end()-1){//vector数组尾作为蛇头,红点
headX = (*it).x();//蛇头坐标
headY = (*it).y();//蛇头坐标
int posHeadX = headX*Bsize+startX;//蛇头像素位置
int posHeadY = headY*Bsize+startY;//蛇头像素位置
if(s.getSdirect() == s_up){
p.drawPixmap(posHeadX-10,posHeadY-15,2*Bsize,2*Bsize,QPixmap("../image/shead_up.png"));
}else if(s.getSdirect() == s_down){
p.drawPixmap(posHeadX-10,posHeadY-5,2*Bsize,2*Bsize,QPixmap("../image/shead_down.png"));
}else if(s.getSdirect() == s_left){
p.drawPixmap(posHeadX-15,posHeadY-10,2*Bsize,2*Bsize,QPixmap("../image/shead_left.png"));
}else if(s.getSdirect() == s_right){
p.drawPixmap(posHeadX-5,posHeadY-10,2*Bsize,2*Bsize,QPixmap("../image/shead_right.png"));
}
}else{//蛇身黑点
int xx=(*it).x()*Bsize+startX;
int yy=(*it).y()*Bsize+startY;
p.drawPixmap(xx,yy,Bsize,Bsize,QPixmap("../image/snake1.png"));
}
if (headX == appleX && headY == appleY){//吃到了!
isEated = true;
}
}
p.end();
}
void init() {//每次游戏开始的数据初始化
for (int i = 0; i < W; i++)
for (int j = 0; j < H; j++)
Smap[i][j] = 0;
s.snakeBody.clear();
s.setSdirect(s_right);
crashed = false;
gameStart = false;
score = 0;
gameStartClickCount = 0;
snakeInit();//初始化蛇身
setRound();//设置第几关
createApple();//随机产生苹果位置
}
void createApple() {//随机产生苹果位置
if (isEated){
player_eat->play();//吃到音效
score += 10;
}
while (true) {
srand((int)time(NULL));
int row = rand() % 21;//产生0~24的随机数
int col = rand() % 25;
if (Smap[col][row] == 0) {
//Smap[row][col] = 1;
appleX = col;
appleY = row;
posAppleX = appleX*Bsize+startX;//像素位置与坐标转换
posAppleY = appleY*Bsize+startY;
break;
}
}
isEated = false;//刚产生的苹果吃不到!
}
void goUp() { //五种情况①撞墙;②撞苹果;③撞自己;④都不撞;⑤撞障碍物
s.setSdirect(s_up);
if (!((headX == appleX) && (headY - 1 == appleY)) && (headY - 1 >= 0) && (Smap[headX][headY - 1] == 0)) {//都不撞(头插一点,尾删一点)
s.snakeBody.push_back(Point(headX, headY - 1));//头插一点
//Smap[headX][headY - 1] = 1;
std::vector::iterator it = s.snakeBody.begin();//找到第一个点(尾部)
Smap[(*it).x()][(*it).y()] = 0;
s.snakeBody.erase(it);//尾删一点
}
else if ((headX == appleX) && (headY - 1 == appleY)) {//撞苹果,吃到(头插一点,尾不删)
s.snakeBody.push_back(Point(headX, headY - 1));//头插一点
//Smap[headX][headY - 1] = 1;
isEated = true;
}else if(Smap[headX][headY - 1]==1){
crashed = true;
return;
}
else if (headY - 1 < 0) {//不是撞苹果->撞墙:GameOver
crashed = true;
return;
}
else{//撞自己
crashed = true;
return;
}
}
void goDown() { //五种情况①撞墙;②撞苹果;③撞自己;④都不撞
s.setSdirect(s_down);
if (!((headX == appleX) && (headY + 1 == appleY)) && (headY + 1 < H) && (Smap[headX][headY + 1] == 0)) {//都不撞(头插一点,尾删一点)
s.snakeBody.push_back(Point(headX, headY + 1));//头插一点
//Smap[headX][headY + 1] = 1;
std::vector::iterator it = s.snakeBody.begin();//找到第一个点(尾部)
Smap[(*it).x()][(*it).y()] = 0;
s.snakeBody.erase(it);//尾删一点
}
else if ((headX == appleX) && (headY + 1 == appleY)) {//撞苹果,吃到(头插一点,尾不删)
s.snakeBody.push_back(Point(headX, headY + 1));//头插一点
//Smap[headX][headY + 1] = 1;
isEated = true;
}
else if(Smap[headX][headY - 1]==1){
crashed = true;
return;
}
else if (headY + 1 >= H) {//撞墙:GameOver
crashed = true;
return;
}
else{//撞自己
crashed = true;
return;
}
}
void goLeft() { //五种情况①撞墙;②撞苹果;③撞自己;④都不撞; ⑤撞障碍物
s.setSdirect(s_left);
if (!((headX - 1 == appleX) && (headY == appleY)) && (headX - 1 >= 0) && (Smap[headX - 1][headY] == 0)) {//都不撞(头插一点,尾删一点)
s.snakeBody.push_back(Point(headX - 1, headY));//头插一点
//Smap[headX - 1][headY] = 1;
std::vector::iterator it = s.snakeBody.begin();//找到第一个点(尾部)
Smap[(*it).x()][(*it).y()] = 0;
s.snakeBody.erase(it);//尾删一点
}
else if ((headX - 1 == appleX) && (headY == appleY)) {//撞苹果,吃到(头插一点,尾不删)
s.snakeBody.push_back(Point(headX - 1, headY));//头插一点
//Smap[headX - 1][headY] = 1;
isEated = true;
}
else if(Smap[headX - 1][headY]==1){
crashed = true;
return;
}
else if (headX - 1 < 0) {//撞墙:GameOver
crashed = true;
return;
}
else{//撞自己
crashed = true;
return;
}
}
void goRight() { //五种情况①撞墙;②撞苹果;③撞自己;④都不撞; ⑤撞障碍物
s.setSdirect(s_right);
if (!((headX + 1 == appleX) && (headY == appleY)) && (headX + 1 < W) && (Smap[headX + 1][headY] == 0)) {//都不撞(头插一点,尾删一点)
s.snakeBody.push_back(Point(headX + 1, headY));//头插一点
//Smap[headX + 1][headY] = 1;
std::vector::iterator it = s.snakeBody.begin();//找到第一个点(尾部)
Smap[(*it).x()][(*it).y()] = 0;
s.snakeBody.erase(it);//尾删一点
}
else if ((headX + 1 == appleX) && (headY == appleY)) {//撞苹果,吃到(头插一点,尾不删)
s.snakeBody.push_back(Point(headX + 1, headY));//头插一点
//Smap[headX + 1][headY] = 1;
isEated = true;
}
else if(Smap[headX][headY - 1]==1){
crashed = true;
return;
}
else if (headX + 1 >= W) {//撞墙:GameOver
crashed = true;
return;
}
else{//撞自己
crashed = true;
return;
}
}
//槽函数
void myWidget::on_pushButton_clicked()
{
gameStart = true;
player->play();
gameStartClickCount++;
if(gameStartClickCount<2)//每次游戏开始按钮只有第一次生效,避免和暂停按钮发生冲突
gamePause = true;
update();
}
void myWidget::on_pushButton_2_clicked()
{
if(gameStartClickCount){//大于1生效,避免和开始按钮发生冲突
gamePause = !gamePause;//取反实现toggle开关
if(gamePause){
ui->pushButton_2->setText("暂停");
}else{
ui->pushButton_2->setText("继续");
}
update();
}
}
void snakeInit(){
s.snakeBody.push_back(Point(3,2));
s.snakeBody.push_back(Point(4,2));
s.snakeBody.push_back(Point(5,2));
s.snakeBody.push_back(Point(6,2));//初始化蛇身从左上角出发
Smap[3][2] = 1,Smap[4][2] = 1,Smap[5][2] = 1,Smap[6][2] = 1;
}
void setRound(){//设置关卡
if(score>=500){//满500分进入下一关
Sround++;
//message:恭喜进入下一关
}
if(Sround == 1){
Smap[6][4]=Smap[6][5]=Smap[6][6]=Smap[6][7]=Smap[6][8]=1;
Smap[6][11]=Smap[6][12]=Smap[6][13]=Smap[6][14]=Smap[6][15]=1;
Smap[18][4]=Smap[18][5]=Smap[18][6]=Smap[18][7]=Smap[18][8]=1;
Smap[18][11]=Smap[18][12]=Smap[18][13]=Smap[18][14]=Smap[18][15]=1;
Smap[7][4]=Smap[8][4]=Smap[9][4]=Smap[10][4]=1;
Smap[14][4]=Smap[15][4]=Smap[16][4]=Smap[17][4]=1;
Smap[7][15]=Smap[8][15]=Smap[9][15]=Smap[10][15]=1;
Smap[14][15]=Smap[15][15]=Smap[16][15]=Smap[17][15]=1;
}else if(Sround == 2){
//第二关待定
}
}
void myWidget::on_pushButton_4_clicked()
{
bg=!bg;//背景切换
update();
}
void myWidget::on_pushButton_5_clicked()
{
if(bgm){
player->stop();
}else{
player->play();
}
bgm = !bgm;
}
#include "mywidget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
myWidget w;
w.show();
return a.exec();
}
运行结果未变