QT开发拼图小游戏
前两天闲来无事,突然想着能不能用QT来实现一个拼图的小游戏呢?于是自己用了一天的时间大概实现了基本的过程,特将自己实现的思路和过程记录于此,以便日后查看和他人参阅,不足之处还望指正!
先看一下程序运行后的效果图(拼图所用素材来自姑射仙子的头像):
本篇博客重点是理一下实现的思路,只贴出大部分核心的代码作出解释,如需完整版,请留言告知!
首先界面的设计来自QT设计师,label的布局采用栅格布局(QGridLayout),当然你也可以不用。界面设计这块无需多言。
下面从以下几个部分展开
1.游戏的初始化
本次设计的拼图游戏是先将一张图片先切分为9个小块,然后随机打乱顺序,显示在label上,下面是头文件"pintu.h"中一些变量的申明,变量的含义都有注明:
pragma once
#include <QtWidgets/QWidget>
#include "ui_QtGuiApplication.h"
#include"qtimer.h"
#include<qevent.h>
#include<iostream>
#include<vector>
#include <algorithm>
#include <random>
#include<time.h>
#include<map>
#include<qmessagebox.h>
using namespace std;
class QtGuiApplication : public QWidget
{
Q_OBJECT
public:
QtGuiApplication(QWidget *parent = Q_NULLPTR);
private:
Ui::QtGuiApplicationClass ui;
QTimer *time1 = new QTimer(this);
vector<QPixmap>image;//保存切分后的图像
map<int, QLabel*>key;//保存图像和对应的位置
vector<int>v = { 0,1,2,3,4,5,6,7,8 };//默认的顺序,之后会被随机打乱
vector<int>vs = { 0,1,2,3,4,5,6,7,8 };//不会变
bool eventFilter(QObject *obj, QEvent *event);//事件过滤器
int flag = 0;//标志位,当我鼠标点击两下时就要交换
pair<int,int>pos;//记录鼠标点击的位置
};
首先向量v和vs代表图片的顺序,v会被打乱,作为游戏的初始化过程,由于我的图片大小是600*600,所以切分后的图像直接就是200*200,当然你最好是利用img.width和img.height来获得图像的长和宽,然后除以3来确定小图像的大小。接着将切分后的图像块载入到容器image中。而lab容器里面则包括9个QLabel对象
# pragma execution_character_set("utf-8") //显示中文
#include "pintu.h"
#include<iostream>
using namespace std;
QtGuiApplication::QtGuiApplication(QWidget *parent)
: QWidget(parent)
{
//这块的目的是要打乱容器元素的顺序,也就是产生一组随机数,让这些图片随机分布
srand((unsigned)time(NULL));
random_shuffle(v.begin(), v.end());
ui.setupUi(this);
QPixmap img,imgs;
img.load("c:\\users\\x\\desktop\\game.jpg");
for (int i = 0; i<3; i++)
{
for (int j = 0; j<3; j++)
{
imgs = img.copy(i * 200, j * 200, 200, 200);
//ui.label_5->setPixmap(imgs);
image.push_back(imgs);
}
}
//vector容器库很强大,啥都能放进去,用着很顺手!
vector<QLabel*> lab ={ui.label_7,ui.label_6,ui.label_9,ui.label_5 ,ui.label_3,ui.label_4, ui.label_8, ui.label_2,ui.label_10 };
//map里面保存的是打乱后的图片的顺序
//将打乱后的容器的顺序作为带拼接的图片信息
for (int n = 0; n < 9; n++)
{
key.insert({ n,lab[n] });
lab.at(n)->setScaledContents(true);
lab.at(n)->installEventFilter(this);//安装时间过滤器
lab.at(n)->setPixmap(image.at(v[n]));
}
}
需要特别注意的是我的image容器里面的图像块和lab容器里面的QLabel对象是一一对应的关系,也就是如果image中的第一个元素显示在lab的第一个元素,第二个显示在第二个中依次类推那么实际上这就是我们最终想要的结果(也就是游戏结束的标志),因此我们首先将向量V打乱顺序,然后将向量V和lab组成一个map,作为界面初始化的显示,我们在点击图片块的时候会调整v的顺序,直到v的顺序和初始的顺序一直,即V==VS时,游戏结束!这块很关键,应该是本程序的核心思想。
2.图像块位置交换
这块我采取的做法是给每个label安装事件过滤器,捕获每个QLabel上的鼠标点击事件,如果点击了两个不同的label,则交换这两个label中的图片。下面我只给出事件过滤器中的一个label中的代码,其余的label和这下面这个类似
/事件过滤器,捕捉label上的鼠标单击事件
bool QtGuiApplication::eventFilter(QObject *obj, QEvent *event)
{
if (obj == ui.label_7)
{
if (event->type() == QEvent::MouseButtonPress)
{
flag=flag+1;
if (flag == 1)
{
pos.first = 0;//当鼠标第一次点击时
}
//如果此时标志位为2,则准备交换,切记交换的时候不仅仅是图像的交换,内部更是向量v的交换
if (flag == 2)
{
pos.second = 0;
if (pos.first == pos.second)
{
QMessageBox::question(this, tr("温馨提示"), tr("老铁,同一个图片点了两次呀!"), QMessageBox::Ok);
flag = 0;
//pos.first = 10;
//pos.second = 11;
}
else
{
key[pos.first]->setPixmap(image.at(v[pos.second]));
key[pos.second]->setPixmap(image.at(v[pos.first]));
flag = 0;
swap(v[pos.first], v[pos.second]);
if (v == vs)
{
QMessageBox::question(this, tr("温馨提示"), tr("恭喜您!完成任务!"), QMessageBox::Ok);
}
}
}
//QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
}
return QObject::eventFilter(obj, event);
}
在这里需要说明一下,pos是C++中的一个pair类型,用来保存鼠标点击的位置,也就是告诉我们鼠标点击了那个图片块,(为什么上面的代码中pos=0呢,前面已经提过,因为我的lab容器中的元素和image是一一对应的关系,而ui.label_7,正好是我lab中的第一个元素,也就是位置0,所以pos=0)。然后将这两张图像块互换位置,一定要记住V中的元素也要交换,毕竟我们的程序最终是根据V中的元素顺序来作为游戏终止的条件的。
flag是一个标志位,鼠标点击一下flag加1,当flag=2时flag置为0,交换两个图片的位置。这样就能保证鼠标点击两次,相应的图片块的互换过程。还有就是如果同一个图片块点击了两次程序会提醒您同一个图像块点击了两次。
3.游戏终止条件
每次交换完图片块都要检查V中元素的顺序,如果v=vs,则拼图完成,挑战成功,游戏结束!
/*************************************************************************************************************/
由于鄙人才学疏浅,加之语言描述能力欠佳,博客有些内容未免显得有些生硬(哈哈,凑合看吧!),有不足之处还望多多指正,有更好的想法也欢迎留言交流!2019/2/23 于学科二楼。
上一篇: 原生js实现贪食蛇小游戏