用Qt写一个迷宫游戏

一段时间以前,我用c写了个使用随机数生成迷宫的代码,现在在自学学Qt,于是就想到了利用这个代码改一个小游戏。
现在记录一下自己的学习过程,也算抛砖引玉,各位看官请多指教。
话不多说现在开始。
使用的是Qt的IDE:QT Creater

step 1:把迷宫生成的c代码用c++类改写

为了方便,先把代码改写成类,诸位有兴趣可以拿去一玩。
代码丑了点,但是能用就行了。

maze.h

#ifndef MAZE_H
#define MAZE_H
/*************************************************** -1:边界 0:墙壁* 1:空单元(初始化后未判定的单元)* 2:待定墙壁(新建已定单元时建立)* 3:已定单元(确定迷宫树已延伸到的单元)* 4:打通墙壁(延伸操作)* 5.起点* 6.终点* 7.已经过路径***************************************************/
class maze {private:int level;  //迷宫阶数int** map;  //地图存储空间int start_x, start_y;   //起终点//生成基础地图(单元格)void base();//使点的周围设为待定(2)void _2(int i, int j);//设定迷宫开始延伸的起点void start() ;//循环停止判定(是否存在未判定的区域)bool judge();//操作(如果相邻空单元(1)则打通(变为4),如果不相邻空单元则为墙壁(0))void op(int i, int j);//随机选择一个待定墙壁判断并操作void random2();
public://构造函数申请内存空间maze(int in_level);~maze();//获取地图int getlevel() ;int** getmap();int getside();//生成地图void makemap();int p_x, p_y; //当前位置//重置地图void rebuildmap();int* operator[](int index);
};
#endif // MAZE_H

maze.cpp

#include "maze.h"
//生成基础地图(单元格)
void maze::base() {for (int i = 0; i < level * 2 + 1; i++) {for (int j = 0; j < level * 2 + 1; j++) {if (i == 0 || j == 0 || i == level * 2 + 1 - 1 ||j == level * 2 + 1 - 1) {map[i][j] = -1;} else if (i % 2 != 0 && j % 2 != 0) {map[i][j] = 1;} else {map[i][j] = 0;}}}
}
//使点的周围设为待定(2)
void maze:: _2(int i, int j) {if (map[i - 1][j] == 0) {map[i - 1][j] = 2;}if (map[i + 1][j] == 0) {map[i + 1][j] = 2;}if (map[i][j - 1] == 0) {map[i][j - 1] = 2;}if (map[i][j + 1] == 0) {map[i][j + 1] = 2;}
}
//设定迷宫开始延伸的起点
void maze:: start() {map[start_x][start_y] = 5;_2(start_x, start_y);
}
//循环停止判定(是否存在未判定的区域)
bool maze:: judge() {bool flag = 0;for (int i = 0; i < level * 2 + 1; i++) {for (int j = 0; j < level * 2 + 1; j++) {if (map[i][j] == 2) {flag = 1;return flag;}}}return flag;
}
//操作(如果相邻空单元(1)则打通(变为4),如果不相邻空单元则为墙壁(0))
void maze:: op(int i, int j) {if ((map[i - 1][j] == 3 || map[i - 1][j] == 5) && map[i + 1][j] == 1) {map[i][j] = 4;map[i + 1][j] = 3;_2(i + 1, j);start_x = i + 1;start_y = j;} else if ((map[i][j - 1] == 3 || map[i][j - 1] == 5) &&map[i][j + 1] == 1) {map[i][j] = 4;map[i][j + 1] = 3;_2(i, j + 1);start_x = i;start_y = j + 1;} else if ((map[i + 1][j] == 3 || map[i + 1][j] == 5) &&map[i - 1][j] == 1) {map[i][j] = 4;map[i - 1][j] = 3;_2(i - 1, j);start_x = i - 1;start_y = j;} else if ((map[i][j + 1] == 3 || map[i][j + 1] == 5) &&map[i][j - 1] == 1) {map[i][j] = 4;map[i][j - 1] = 3;_2(i, j - 1);start_x = i;start_y = j - 1;} else {map[i][j] = 0;}
}
//随机选择一个待定墙壁判断并操作
void maze:: random2() {int t = 0;for (int i = 0; i < level * 2 + 1; i++) {for (int j = 0; j < level * 2 + 1; j++) {if (map[i][j] == 2) {t++;}}}int k = rand() % t + 1;t = 0;for (int i = 0; i < level * 2 + 1; i++) {for (int j = 0; j < level * 2 + 1; j++) {if (map[i][j] == 2) {t++;if (t == k) {op(i, j);goto loopout;}}}}
loopout:if (!judge()) {map[start_x][start_y] = 6;}
}//构造函数申请内存空间
maze:: maze(int in_level) : level(in_level) {map = new int* [level * 2 + 1];for (int i = 0; i < level * 2 + 1; i++) {map[i] = new int[level * 2 + 1];}start_x = 1, start_y = 1; //起点设置为(1,1)
}
maze::~maze() {for (int i = 0; i < level * 2 + 1; i++) {delete [] map[i];}delete [] map;
}
//获取地图
int maze:: getlevel() {return maze::level;
}
int** maze::getmap() {return map;
}
int maze::getside() {return level * 2 + 1;
}
//生成地图
void maze:: makemap() {p_x = start_x;p_y = start_y;base();start();int a = 0;while (judge()) {a++;random2();// if (a % 30 == 0) {//     printarr(map, level);//     system("PAUSE");// }}
}
int* maze::operator[](int index) {return map[index];
}
//重置地图
void maze::rebuildmap(){start_x=1;start_y=1;makemap();
}

好现在就可以生成一个迷宫了。
我把一开始的栅格大小叫做迷宫的阶数,现在可以通过构造函数生成特定特定阶数的迷宫,这阶数就可以变相的看作难度。

step 2:创建ui

首先我们创建一个Qt widgets application 项目,叫maze好了。
类名就叫mazeWidget,然后基类选择QWidget,剩下默认即可。
然后修改mazewidget.ui。

ui

先把主窗口字体改成14号。
拖入7个PushButton,5个Label,三个Spacer。
然后分别把对象名(objectName)、显示文本(text)改成:

QPushButton:start_btn 开始游戏stop_ptn 暂停end_btn 终止游戏rule_btn 规则setting_btn 设置battle_btn 双人对战about_btn 关于
QLabel:plaque 迷宫游戏plaque_time 时间:time_value 0plaque_grade 分数:grade_value 0

然后修改对象属性,其中暂停、终止游戏、双人对战按钮默认禁用(enabled = false)。
各个标签文字水平、垂直分别居中显示(修改alignment)。
然后选中这些按钮,应用垂直的布局器。
排列成这个样子:

现在再拖入一个Frame、一个ProgressBar,也应用垂直布局器。
ProgressBar隐藏文字(textVisible = false)。
Frame设置边框形状(frameShape)为Box,边框阴影(frameShadow)为Raised。
最后给整个窗口应用水平布局器,效果应该是这样的:

这里先不用管界面风格

ok,那么到此ui就完成了,接下来我们来写逻辑。

step 3:窗口逻辑

第一步:在项目中加入我们的迷宫类文件。

加入好后如图所示:

第二步:我们的窗口类头文件的编写

首先是头文件的加入:

#include <QMessageBox>      //Qt信息窗口头文件
#include <QPainter>         //Qt绘图头文件
#include <QDebug>           //QtDebug头文件
#include <QKeyEvent>        //Qt按键事件头文件
#include <QTimer>           //Qt计时器头文件
#include <QInputDialog>     //Qt输入对话框头文件
#include "maze.h"           //迷宫类头文件

其次是函数的声明:

protected:void paintEvent(QPaintEvent*);        //绘图事件void keyPressEvent(QKeyEvent*);       //按键按下事件
private slots:void on_start_btn_clicked();    //|void on_stop_ptn_clicked();     //|void on_end_btn_clicked();      //|void on_rule_btn_clicked();     //|各按钮点击槽函数void on_setting_btn_clicked();  //|void on_battle_btn_clicked();   //|void on_about_btn_clicked();    //|void time_update();             //时间更新槽函数

其中绘图时间与按键按下事件是继承自QWidget类的虚函数,实现了绘制迷宫地图和响应键盘按下的功能,绘图事件函数在窗口移动、改变大小、调用repaint()函数等情况是自动调用,键盘按下事件函数在键盘按下时调用,这两个函数名称不可以修改。
之后的7个函数是我们ui按钮点击时调用的槽函数,可以通过设计界面右击对应的按钮,点击转到槽来自动生成函数的声明和实现(定义)的框架,在这里是自动生成的函数名,不可以更改。
最后是我们自己定义的时间更新的槽函数,在计时器(QTimer)发出timeout()信号时调用,由于是自己定义的函数,名称可以修改,只要在代码中做对应修改就可以,需要手动对信号和槽进行链接。

最后是窗口类成员变量的声明:

private:Ui::mazeWidget* ui;         //ui对象指针maze* map;                  //迷宫对象指针bool painting_switch;       //绘图开关bool timing_switch;         //计时开关bool keybord_switch;        //键盘响应开关bool stop_switch;           //暂停按钮状态int grade;                  //分数int time;                   //时间QTimer* timer;               //计时器对象指针

其中ui对象指针不需要我们写,Qt自动生成。
迷宫对象与计时器对象指针用来实现迷宫与计时对应功能。
剩余变量用来存储游戏运行中状态与数据。

那么好,窗口类头文件的编写就完成了。
接下来是各函数的实现

第三步:窗口类源文件的编写

首先需要添加头文件"mazewidget.h"

#include "mazewidget.h"
#include "ui_mazewidget.h"

接下来开始挨个编写函数:

1.构造函数

#include "mazewidget.h"
#include "ui_mazewidget.h"
mazeWidget::mazeWidget(QWidget* parent): QWidget(parent), ui(new Ui::mazeWidget), map(new maze(20)), painting_switch(false), timing_switch(false), keybord_switch(false), grade(0), time(0) {//TODO:状态栏ui->setupUi(this);ui->progressBar->setVisible(false);                 //初始隐藏进度条ui->end_btn->setEnabled(false);                     //设置终止按钮禁用ui->stop_ptn->setEnabled(false);                    //设置暂停按钮禁用ui->grade_value->setText("  ");                     //设置分数值显示为空ui->time_value->setText("  ");                      //设置时间值显示为空map->makemap();                                     //生成地图timer = new QTimer(this);                                           //初始化计时器connect(timer, &QTimer::timeout, this, &mazeWidget::time_update);   //链接时间更新信号与槽
}

2.析构函数

加入两行:

mazeWidget::~mazeWidget() {delete ui;delete map;     //1.delete timer;   //2.
}

3.绘图事件虚函数

void mazeWidget::paintEvent(QPaintEvent*) {if(!painting_switch) return;QPainter painter(this);     //画笔对象//绘图逻辑:int perblock = (std::min(ui->frame->width(), ui->frame->height()) - 20) / (map->getside());int start_x = ui->frame->x() + (ui->frame->width() - (ui->frame->x() + (map->getside()) * perblock)) / 2;int strat_y = ui->frame->y() + (ui->frame->height() - (ui->frame->y() + (map->getside()) * perblock)) / 2;for(int i = 0; i < map->getlevel() * 2 + 1; i++) {for(int j = 0; j < map->getlevel() * 2 + 1; j++) {if(i == map->p_x && j == map->p_y) {painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::red));} else if(map->getmap()[i][j] == 7) {painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::yellow));} else if(map->getmap()[i][j] == 3 || map->getmap()[i][j] == 4) {painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::gray));} else if(map->getmap()[i][j] == 5) {painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::blue));} else if(map->getmap()[i][j] == 6) {painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::green));} else {painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::black));}}}
}

首先判断绘图开关是否打开。
这里创建一个画笔对象用于绘制,我想使地图始终保持在frame的中央,所以计算了每个块的像素perblock,绘图开始的位置start_x与start_y,然后用painter的成员函数fillRect()对每个块分别涂色。
黑色为墙壁、边界,灰色为可通行路径,蓝色与绿色分别为起点和终点,红色是所在的位置,黄色是经过的路径。
然后使用两层循环涂色。

4.键盘按下事件虚函数

键盘按下时间将处理角色移动。

void mazeWidget::keyPressEvent(QKeyEvent* event) {if(!keybord_switch) return;int x = map->p_x;int y = map->p_y;//键盘移动逻辑:if(event->key() == Qt::Key_I || event->key() == Qt::Key_W) {if((*map)[x][y - 1] == 3 || (*map)[x][y - 1] == 4 || (*map)[x][y - 1] == 5 || (*map)[x][y - 1] == 6 || (*map)[x][y - 1] == 7) {map->p_y--;}} else if(event->key() == Qt::Key_K || event->key() == Qt::Key_S) {if((*map)[x][y + 1] == 3 || (*map)[x][y + 1] == 4 || (*map)[x][y + 1] == 5 || (*map)[x][y + 1] == 6 || (*map)[x][y + 1] == 7) {map->p_y++;}} else if(event->key() == Qt::Key_J || event->key() == Qt::Key_A) {if((*map)[x - 1][y] == 3 || (*map)[x - 1][y] == 4 || (*map)[x - 1][y] == 5 || (*map)[x - 1][y] == 6 || (*map)[x - 1][y] == 7) {map->p_x--;}} else if(event->key() == Qt::Key_L || event->key() == Qt::Key_D) {if((*map)[x + 1][y] == 3 || (*map)[x + 1][y] == 4 || (*map)[x + 1][y] == 5 || (*map)[x + 1][y] == 6 || (*map)[x + 1][y] == 7) {map->p_x++;}}//经过路径if((*map)[map->p_x][map->p_y] != 5 && (*map)[map->p_x][map->p_y] != 6)(*map)[map->p_x][map->p_y] = 7;repaint();//到达终点if((*map)[map->p_x][map->p_y] == 6) {map->makemap();repaint();grade += pow(map->getlevel(), 2);ui->grade_value->setText(QString::number(grade));}
}

这里使用传入参数event来判断按下的是什么按键,然后判断对应方向是否能进行移动,如果能,改变map的p_x与p_y。
同时经过的路径数值改为7,视作已经经过。
如果当前位置与终点重合,即生成下一轮地图,计分数为当前迷宫阶数的平方,并更新记分标签控件。

5.时间更新槽函数

void mazeWidget::time_update() {if(time != 0) {//计时中time--;ui->time_value->setText(QString::number(time));ui->progressBar->setValue(time / 2);} else {timer->stop();                          //停止计时器ui->progressBar->setVisible(false);     //隐藏进度条keybord_switch = false;                 //设置键盘响应、painting_switch = false;                //绘图响应、timing_switch = false;                  //计时响应为关闭状态repaint();                              //清除画布ui->start_btn->setEnabled(true);        //|ui->time_value->setText(" ");           //|ui->grade_value->setText(" ");          //|ui->stop_ptn->setEnabled(false);        //|设置各按钮与标签状态ui->end_btn->setEnabled(false);         //|ui->setting_btn->setEnabled(true);      //|//提示QMessageBox outgrade(QMessageBox::NoIcon, "恭喜", "您得分:" + QString::number(grade), QMessageBox::Ok);outgrade.exec();//分数重置grade = 0;}
}

计时器将在开始按钮按下时被设置为1000个周期,即一秒发出一次timeout()信号。
游戏共计时200秒,故进度条值为time / 2。同时计时停止时进行游戏停止的对应ui设置,并弹出消息窗口显示分数。

6.各按钮点击槽函数

void mazeWidget::on_start_btn_clicked() {painting_switch = true;timing_switch = true;keybord_switch = true;time = 200;timer->start(1000);ui->progressBar->setVisible(true);ui->progressBar->setValue(100);repaint();ui->time_value->setText(QString::number(time));ui->grade_value->setText(QString::number(grade));ui->start_btn->setEnabled(false);ui->stop_ptn->setEnabled(true);ui->end_btn->setEnabled(true);ui->setting_btn->setEnabled(false);
}
void mazeWidget::on_stop_ptn_clicked() {if(stop_switch) {timing_switch = false;keybord_switch = false;timer->stop();ui->stop_ptn->setText("继续");stop_switch = false;} else {timing_switch = true;keybord_switch = true;timer->start();ui->stop_ptn->setText("暂停");stop_switch = true;}
}
void mazeWidget::on_end_btn_clicked() {timing_switch = false;painting_switch = false;keybord_switch = false;stop_switch = false;timer->stop();time = 0;grade = 0;ui->progressBar->setVisible(false);ui->grade_value->setText(" ");ui->time_value->setText(" ");ui->stop_ptn->setText("暂停");ui->stop_ptn->setEnabled(false);ui->end_btn->setEnabled(false);ui->start_btn->setEnabled(true);ui->setting_btn->setEnabled(true);map->rebuildmap();repaint();}
void mazeWidget::on_rule_btn_clicked() {QMessageBox rule(QMessageBox::NoIcon, "规则", "计时200秒,根据迷宫等级与经过关卡记分。\n操作方式:WASD或者IJKL控制方向。", QMessageBox::Ok);rule.exec();
}
void mazeWidget::on_setting_btn_clicked() {QStringList difficultys;difficultys << tr("小朋友启蒙难度(5阶迷宫)") << tr("简单难度(10阶迷宫)") << tr("普通难度(20阶迷宫)") << tr("困难难度(40阶迷宫)");QString difficulty = QInputDialog::getItem(this, tr("选择难度"),tr("请选择一个条目"), difficultys, 0, false);if(difficulty == tr("小朋友启蒙难度(5阶迷宫)")) {delete map;map = new maze(5);map->makemap();} else if(difficulty == tr("简单难度(10阶迷宫)")) {delete map;map = new maze(10);map->makemap();} else if(difficulty == tr("普通难度(20阶迷宫)")) {delete map;map = new maze(20);map->makemap();} else if(difficulty == tr("困难难度(40阶迷宫)")) {delete map;map = new maze(40);map->makemap();}
}
void mazeWidget::on_battle_btn_clicked() {}
void mazeWidget::on_about_btn_clicked() {QMessageBox about(QMessageBox::NoIcon, "关于", "由amazcuter制作,代码请访问我的CSDN博客。\n玩得开心。\n\n双人对战模式未开发完成,将会在未来版本开放。", QMessageBox::Ok);about.exec();
}

分别进行对应的ui设置,功能调用。

step 4.主函数

#include "mazewidget.h"     //窗口类头文件
#include "maze.h"           //迷宫类头文件
#include <QApplication>
#include <QStyleFactory>    //界面风格头文件int main(int argc, char* argv[]) {srand(time(NULL));                              //设置随机数种子QApplication a(argc, argv);a.setStyle(QStyleFactory::create("Fusion"));    //更改界面风格mazeWidget w;w.show();return a.exec();
}

这里加入头文件,设置一下界面风格和随机数种子。
双人模式放在之后更新吧,现在先放着。
好了现在项目就算编写好了。

成果:


转载请表明出处哈,我还是很虚荣的233。
持续更新,我打算把这搞成我的实训作业,努力添加功能,优化外观。
当前版本v2.0。
觉得还行的看官点个攒再走吧…

用Qt写一个迷宫游戏相关推荐

  1. 使用Qt写一个简单的五子棋单机小游戏

    使用Qt写一个简单的五子棋单机小游戏 刚学,不够专业请勿喷,有不对的地方还请指出,我渴望进步!现在贴出这个游戏我原创的所有代码.希望可以帮到有需要的人. 游戏界面: 因为后面添加了.wav的音频文件, ...

  2. python可视化迷宫求解_如何用 Python 制作一个迷宫游戏

    相信大家都玩过迷宫的游戏,对于简单的迷宫,我们可以一眼就看出通路,但是对于复杂的迷宫,可能要仔细寻找好久,甚至耗费数天,然后可能还要分别从入口和出口两头寻找才能找的到通路,甚至也可能找不到通路. 虽然 ...

  3. python俄罗斯方块算法详解_用 Python 写一个俄罗斯方块游戏 (

    @@ -2,34 +2,34 @@ > * 原文作者:[Dr Pommes](https://medium.com/@pommes) > * 译文出自:[掘金翻译计划](https://g ...

  4. python写游戏脚本-使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  5. python编程小游戏-使用Python写一个小游戏

    引言 最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下 ...

  6. linux下Qt编写串口调试助手,如何在linux下用QT写一个简单的串口调试助手

    如何在linux下用QT写一个简单的串口调试助手 QT5串口类 在QT5以前,编写串口一般使用的是qextserialport类,但在QT5之后有了QT自带的串口类SerialPort(串口基础类)和 ...

  7. Python 写一个俄罗斯方块游戏

    使用 Python 的 PyGame 库写一个俄罗斯方块游戏的逐步指南 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例 ...

  8. 使用python制作聊天框解谜游戏_使用Python写一个小游戏alien invasion!

    最近python语言大火,除了在科学计算领域python有用武之地之外,在游戏.后台等方面,python也大放异彩,本篇博文将按照正规的项目开发流程,手把手教大家写个python小游戏,来感受下其中的 ...

  9. 手机版python3h如何自制游戏_教你如何用 Python 写一个小游戏

    教你如何用 Python 写一个小游戏 引言 最近 python 语言大火, 除了在科学计算领域 python 有用武之地之外, 在游戏后台等方面, python 也大放异彩, 本篇博文将按照正规的项 ...

最新文章

  1. OpenCV3实现人脸识别(三)——训练与识别自己的人脸数据
  2. 一次群晖中勒索病毒后的应急响应
  3. JavaScript对象的chapterI
  4. 农村初中学校计算机课的意义,关于农村学校计算机课件使用的反思.pdf
  5. [美丽的烦恼] SQL删除某些字段重复的记录(只保留一条)
  6. memcached客户端_Memcached集群的使用
  7. 如何将数据库中的记录表导入到VC中
  8. python---django中form组件(1)简单使用和字段了解
  9. 如何在集合中巧用Where来查找相关元素
  10. wps2000集成办公系统 v3.02.99专业版(附序列号)|wps2000老版本
  11. android在adb下刷机教程,adb sideload刷机教程
  12. jsmind 线条_jsMind思维导图模式数据展示
  13. java小球下落_java基础-小球下落问题
  14. H5页面 iPhoneX适配
  15. Python中Selenium模块的使用
  16. Go:DepthFirstSearch深度优先搜索算法(附完整源码)
  17. 计算机软件水平考试什么题型,计算机软考考什么内容
  18. STM32 FSMC TFT LCD
  19. python 的 return 详解
  20. 关于Synaptics.exe感染型病毒

热门文章

  1. 在Windows下和Linux下如何查看网卡MAC地址、以及修改MAC地址
  2. python抽样_python数据预处理 :数据抽样解析
  3. linux ubuntu 22.04双屏显示颜色变绿问题解决
  4. 一个让人灵光一闪的数组C++类Array设计,可以此作为范本进行其他的C++类编写
  5. 【SpringBoot】学生信息查询案例
  6. mysql数据库双向实时同步
  7. 集线器交换机共享带宽独享带宽的理解
  8. R中的Box-Cox变换
  9. OJ实验5-8 使用函数求圆台体积OJ
  10. uni-app的总结