打地鼠QT实现

个人记录!!

平台: Windows + Qt Creator 4.5

一、项目简介

额…就是打地鼠嘛!!!!

做出来之后的界面:

  • 点击开始:界面开始弹出地鼠,本例采用的是图片随机切换的方式,每次随机切换1~3张图片。鼠标成功单击有地鼠的图片之后右上角分数栏会增加得分。每只地鼠有且只能击中一次。
  • 点击暂停:地鼠停止弹出,游戏暂停。
  • 点击停止:地鼠停止弹出,游戏结束,清空分数。

需要准备:

  • 背景图片1(仅洞口)
  • 背景图片2(有老鼠)
  • 背景图片3(老鼠被打)
  • 锤子(举起与按下两张)

如下:(后两张老鼠自己瞎照着画的。。。)

二、准备阶段

1、动态2D显示容器:QGraphicsView

    QGraphicsItem图元 ⊆ QGraphicsScene场景 ⊆ QGraphicsView容器
     容器即是mainwindow,场景即是myScene,图元即是myItem。将要显示的图片封装到item里面,多个item共同组成场景scene,最后将场景scene显示到容器里。更多的知识点建议查看Qt Creator的Help文档(真的很强大,比度娘还靠谱!

2、新建项目

  继承自MainWindow    
    ui布局如下:ui里这块空白的区域,其实就是先新建一个QGraphicsView容器。

3、创建场景类myScene

  右击->添加新文件->C++->C++ Class,Class name:myScene,Base class:QObjict(回头在文件里手动修改基类为QGraphicsScene)->继续->完成。
    进入myscene.h,修改头文件如下:

#ifndef MYSCENE_H
#define MYSCENE_H

#include <QObject>
#include <QGraphicsScene>

class myScene : public QGraphicsScene
{
    Q_OBJECT
public:
    explicit myScene(QObject *parent = nullptr);
    
signals:

public slots:
 };
#endif // MYSCENE_H

    进入myscene.cpp,修改如下:

#include "myscene.h"

myScene::myScene(QObject *parent) : QGraphicsScene(parent)
{
}

4、创建图元类myItem

  右击->添加新文件->C++->C++ Class,Class name:myItem,Base class:直接在输入框输入:QGraphicsPixmapItem->继续->完成。
    进入myitem.h,包含头文件:

#ifndef MYITEM_H
#define MYITEM_H

#include <QGraphicsPixmapItem>

class myItem : public QGraphicsPixmapItem
{
public:
    myItem();
};

#endif // MYITEM_H

三、背景图片布局

1、新建Resources

     项目右击->添加新文件->Qt->Qt Resource File(资源文件,管理外部资源)->名称:img
     回到主界面,项目框新建了Resources文件夹,展开,右击img.qrc->Open in Editor:
          添加->添加前缀:前缀(虚拟文件夹):/bg;添加->添加文件:添加背景图片(3张),/ 表示当前路径。
          添加->添加前缀:前缀(虚拟文件夹):/mouse;添加->添加文件:添加锤子图片(2张)。

2、图元添加

     进入myitem.cpp里面,修改如下:

#include "myitem.h"
#include <QPixmap>

myItem::myItem()
{
    this->setPixmap(QPixmap(":/bg/bg.png"));
}

     这样就成功将背景添加到一个图元里了。接下来要把图元封装到场景里面:在场景头文件myscene.h中包含图元头文件:

#include "myitem.h"

     并在场景类myScene中创建一个private声明表示该类有一个私有的成员变量:

	myItem *item;

      进入myscene.cpp:在构造函数里添加(这样就声明了一个图元,并把该图元放入了场景里面):

	this->item = new myItem;
	this->addItem(this->item);

     接下来就该把场景交给容器去显示,而容器在mianwindow里面。
     进入mainwindow.h,首先包含场景头文件myscene.h,同时private里添加一个成员变量:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "myscene.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    myScene *sc;
};

#endif // MAINWINDOW_H

     进入mianwindows.cpp:构造函数里new出场景对象并显示:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->sc = new myScene;
    this->ui->graphicsView->setScene(sc);
 }
 
 MainWindow::~MainWindow()
{
    delete ui;
}

     此时运行就可以在界面上显示出我们的背景图片了,不过这还是远远不够滴。。。因为我们要显示的是16张一样的背景图片。

     那么怎么添加剩下的背景呢?

进入myscene.h,将场景类myScene中创建的private成员变量:

	myItem *item;

  修改为:

	myItem *item[16];//16个item的数组

  来到myscene.cpp的场景构造函数里面,使用循环将图元放入场景:

#include "myscene.h"

myScene::myScene(QObject *parent) : QGraphicsScene(parent)
{
    //背景图片尺寸
    int width = 160;
    int height = 160;
    //一共设置16张
    for(int i = 0; i < 16; i++){
        this->item[i] = new myItem;
        this->item[i]->setPos(i/4 * width,i%4 *height);

        this->addItem(this->item[i]);
}

  这样一个4*4的背景布局就完成了:

四、地鼠弹出

1、弹出方法

  实现地鼠弹出大体思路:利用定时器触发槽函数,每隔一段时间就将背景图片切换为地鼠图片。而切换图片的方法与之前设置图片的方法类似。在图元类头文件myitem.h中,包含头文件

#include <QString>//添加路径用

  并在类声明public里面定义切换图片的函数:

void setPic(QString path);//用于切换路径->切换图片

  并实现:

void myItem::setPic(QString path){
    this->setPixmap(QPixmap(path));
}

2、定时器设置

  场景头文件myscene.h里面,先包含定时器头文件

#include "myitem.h"

  场景声明里面,包含一个定时器指针以及信号设置:

public slots:
	void showMouse();
private:
	QTimer *ptimer;//定时器

  myscene.cpp里面,new出定时器,并连接定时器与槽函数(用于显示地鼠):

	this->ptimer = new QTimer;
	//换图片:定时器决定隔多久换图片,随机值确定换哪个图片
	connect(this->ptimer,SIGNAL(timeout()),this,SLOT(showMouse()));

  myscene.cpp里面,当然也包含该函数的实现,但是该函数的实现需要随机数,所以事先需要设置随机数种子。

void myScene::showMouse()
{
    //每次显示地鼠之前,都把之前显示的重置掉
    for(int i = 0;i < 16;i++){
        this->item[i]->setPic(":/bg/bg.png");
   }
    //随机一个数字0-15,换相应的图片
    //随机1-3:弹出老鼠个数
    int count = rand()%3 + 1;
    for(int i = 0; i < count; i++){
        int index = rand()%16; //取值0-15
        this->item[index]->setPic(":/bg/bg1.png");//切换图片,显示地鼠
   }
}

  main.cpp里面,包含随机数头文件

#include <stdlib.h>
#include <time.h>

  并设置随机数种子:

	srand((unsigned)time(NULL));

  现在就可以弹出地鼠了:

五、事件重写(实现打地鼠)

  现在虽然可以弹出地鼠图片,但是点击地鼠图片程序是不会做出反应的!!!
  Creator的help文档里面,找到QWidget->Protected Functions->
               virtual void mousePressEvent(QMouseEvent event)
   这个就是我们要重写的事件:鼠标按下事件。
   鼠标点击是在图元上面的,所以要进入myitem.h,声明:

public:
	void mousePressEvent(QGraphicsSceneMouseEvent *event);//鼠标按下事件重写
	void setMouse(bool mouse);
    	bool isMouse();//判断是不是地鼠的图片,有为true;点击图片时,检测该值就知道是不是打到了地鼠
private:
	bool mouse;

   进入myitem.cpp,实现重写函数,同时设定一个标志位:

void myItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
	if(this->isMouse()){
            	this->setPixmap(QPixmap(":/bg/bg2.png"));//打到了地鼠,切换到第三张地鼠被打的背景图片
            	this->mouse = false;//一个地鼠只能打一次
        }
}
void myItem::setMouse(bool mouse){
    this->mouse = mouse;
}
bool myItem::isMouse(){
    return mouse;
}

   同时myscene.cpp中的showMouse函数中,显示地鼠时,将mouse值set为true,反之同理。

void myScene::showMouse(){
    for(int i = 0;i < 16;i++){
        this->item[i]->setPic(":/bg/bg.png");
        this->item[i]->setMouse(false);//设置标志为假,表示么有地鼠
    }
    int count = rand()%3 + 1;
    for(int i = 0; i < count; i++){
        int index = rand()%16; 
        this->item[index]->setPic(":/bg/bg1.png");
        this->item[index]->setMouse(true);//设置为true,表示有地鼠
    }
}

六、按钮功能实现

  这儿使用connect来实现按钮连接(最好不要使用右击转到槽)

connect有个缺点就是不方便设置按钮 setEnabled(true or false),而这个功能个人觉得在这个程序逻辑里还是蛮重要的。

  这儿要注意:开始等按钮位于mainwindow里面,而游戏是否开始需要在场景(myscene)里操作。
  所以,在myscene.h里面,声明:

public slots:
	void startGame();//用于启动定时器ptimer
    	void pauseGame();
    	void stopGame();

  所以,在myscene.cpp里面实现:

void myScene::startGame(){
    this->ptimer->start(1000);
}

void myScene::pauseGame(){
    this->ptimer->stop();
}


void myScene::stopGame(){
    this->ptimer->stop();
    //还原界面
    for(int i = 0;i < 16;i++){
        this->item[i]->setPic(":/bg/bg.png");
        this->item[i]->setMouse(false);//设置标志为假,表示么有地鼠    }
}

  回到mainwindow.cpp里面:

	//发送者在mianwindow,接收者在scene
	connect(this->ui->btn_start,SIGNAL(clicked(bool)),this->sc,SLOT(startGame()));
    connect(this->ui->btn_pause,SIGNAL(clicked(bool)),this->sc,SLOT(pauseGame()));
    connect(this->ui->btn_stop,SIGNAL(clicked(bool)),this->sc,SLOT(stopGame()));

   但是现在存在一个问题,即使在暂停状态下,点击地鼠依然会有相应,所以需要设置一个状态量,来判断是否处于开始状态,只有处于开始状态下,才可以打地鼠!
   进入myitem.h,声明:

public:
    id setStart(bool start);
    bool isStart();
    
private:
       bool start;//用于设置只有在开始状态,才可以打老鼠

   实现:

void myItem::setStart(bool start){
    this->start = start;
}

bool myItem::isStart(){
    return start;
}

  在鼠标按下事件,单击开始、暂停、停止按钮实现函数中,均设置以下start状态量,这儿不再一一贴代码了。

七、分数添加实现

  注意:分数显示与设置是在mainwindow里面实现的,而有没有击中地鼠是在图元myitem里进行判断的。所以要有一种方式把是否击中地鼠这个事件发送到主窗口。可以采取:

  • 设置全局变量:不推荐,效率低,延迟
  • 单例:永远只会产生一个对象,也只能被获取到这一个对象

这里采用单例模式实现:将构造函数设为私有, 通过调用static 成员函数生成对象

关于c++单例:C++ 单例模式总结与剖析 - 行者孙 - 博客园
单例模式的编写:

a) 构造函数设为私有,此时无法生成对象

b) 编写一个成员函数来生成对象,但是无法调用。

c) 将该函数设为static,此时可以生成对象,但是对象不唯一。

d) 添加一个static指针成员,仅当该指针为NULL(也就是第一次访问时)才去生成对象。

e) 但是此时的代码在多线程环境下存在竞态问题。。。不过这儿并不涉及

总之就是添加了一个单例,在单例里面实现分数的添加。

  最后还有一个细节就是,之前设置停止游戏的时候,是没有实现分数的清零的。因为停止游戏是在场景myscene里实现的,但是分数在主窗口,是不可以在场景里面把分数清空的。可以这样设置:
  mainwindow.h里面,声明:

private slots:
    void clearScore();

  mainwindow.cpp里面,实现:

void MainWindow::clearScore(){
    this->ui->lcdNumber->display("0");
}

  并连接(qt里一个按钮是可以绑定多个槽函数的):

	connect(this->ui->btn_stop,SIGNAL(clicked(bool)),this,SLOT(clearScore()));

  除此之外鼠标可以设置为锤子的图片,不再赘述,留下又菜又累的泪水 -_-。

  CSDN戳我下载
  度娘网盘戳我下载,提取码kmuw

  • 15
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是在 Linux 和 Qt实现的简单打地鼠游戏代码,你可以参考: mainwindow.h 文件: ``` #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTimer> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: Ui::MainWindow *ui; QTimer *timer; int score; private slots: void showMole(); void hitMole(); }; #endif // MAINWINDOW_H ``` mainwindow.cpp 文件: ``` #include "mainwindow.h" #include "ui_mainwindow.h" #include <QLabel> #include <QPixmap> #include <QRandomGenerator> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), score(0) { ui->setupUi(this); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(showMole())); connect(ui->label, SIGNAL(clicked()), this, SLOT(hitMole())); } MainWindow::~MainWindow() { delete ui; } void MainWindow::showMole() { int x = QRandomGenerator::global()->bounded(300); int y = QRandomGenerator::global()->bounded(200); ui->label->move(x, y); ui->label->setPixmap(QPixmap(":/images/mole.png")); ui->label->show(); timer->start(1000); } void MainWindow::hitMole() { if (ui->label->pixmap() != nullptr) { score++; ui->label->setPixmap(QPixmap(":/images/hit.png")); QTimer::singleShot(500, ui->label, [=](){ ui->label->setPixmap(QPixmap()); }); } ui->lcdNumber->display(score); } ``` 在上面的代码中: - 在 showMole() 函数中,通过 QRandomGenerator::global() 随机生成地鼠出现的位置,并设置 ui->label 的位置和显示地鼠图片,定时器 timer 每隔一定时间启动一次 showMole() 函数。 - 在 hitMole() 函数中,判断点击的位置和地鼠位置是否重叠,如果重叠则得分加 1,同时设置 ui->label 显示被打中的图片,通过定时器 QTimer::singleShot() 实现打中地鼠地鼠消失的动画效果。 在项目中,还需要添加地鼠图片和被打中的图片(命名为 mole.png 和 hit.png),并在 .qrc 文件中添加资源。 最后,编译运行程序即可实现简单的打地鼠游戏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值