• Qt planeGame day10


    Qt planeGame day10

    Game基本框架

    • qt中没有现成的游戏框架可以用,我们需要自己搭框架
    • 首先创建一个QGame类作为框架,这个基本框架里面应该有如下功能:
    • 游戏初始化
    void init(const QSize& siez,const QString& title);
    
    • 1
    • 游戏反初始化(清理)
    void clean();
    
    • 1
    • 更新游戏
    void update(int);
    
    • 1
    • 渲染游戏
    void render(QPainter* painter);
    
    • 1
    • 游戏是否在运行
    bool isRunning() const;
    
    • 1
    • 退出游戏
    void quit();
    
    • 1
    • 运行游戏
    void runGame();
    
    • 1
    • 设置游戏帧率
    void setFps(qreal fps);
    
    • 1
    • 一个游戏肯定要在一个主循环里面,在Qt中肯定不能使用死循环,就得使用定时器了,基本的变量
    	//控制游戏进行的变量
    	bool m_isRunning = false;
    	//游戏的主循环
    	QTimer* m_mainLoopTimerP{};
    	//游戏帧率60帧
    	qreal m_fps = 1000 / 60;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 基本框架思路:判断游戏是否运行中,运行中的话需要连接定时器处理游戏更新于绘图,然后开启定时器,因为qt的绘图只能在事件中完成,所以把渲染写在事件中即可,在定时器中去调用父类的更新画面
    • 基本框架

    QGame.h

    #ifndef QGAME_H_
    #define QGAME_H_
    
    #include 
    #include 
    
    class QGame :public QWidget
    {
    	Q_OBJECT
    public:
    	QGame(QWidget* parent = nullptr);
    	~QGame();
    	//游戏初始化
    	void init(const QSize& size, const QString& title);
    	//游戏反初始化
    	void clean();
    	//更新游戏,站位符与父类的方法做区别
    	void update(int);
    	//渲染游戏
    	void render(QPainter* painter);
    	//游戏是否进行
    	bool isRuning() const;
    	//退出游戏
    	void quit();
    	//运行游戏
    	void runGame();
    	//设置游戏帧率
    	void setFps(qreal fps);
    
    	//获取游戏帧数的接口
    	qreal fps() const { return m_fps; }
    protected:
    	void paintEvent(QPaintEvent* ev) override;
    private:
    	//控制游戏进行的变量
    	bool m_isRunning = false;
    	//游戏的主循环
    	QTimer* m_mainLoopTimer{};
    	//游戏帧率60帧
    	qreal m_fps = 1000 / 60;
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    QGame.cpp

    #include "QGame.h"
    #include 
    #include 
    QGame::QGame(QWidget* parent) :QWidget(parent)
    	, m_mainLoopTimer(new QTimer)
    {
    
    }
    QGame::~QGame()
    {
    	//清理
    	clean();
    }
    //游戏初始化
    void QGame::init(const QSize& size, const QString& title)
    {
    	//设置窗口固定大小
    	setFixedSize(size);
    	//设置标题
    	setWindowTitle(title);
    
    	//因为上面会有各种资源的初始化,如果初始化失败,那么m_isRunning=false;游戏不继续运行了
    	//初始化成功就为true 
    	m_isRunning = true;
    }
    //游戏反初始化
    void QGame::clean()
    {
    }
    //更新游戏
    void QGame::update(int)
    {
    }
    //渲染游戏
    void QGame::render(QPainter* painter)
    {
    }
    //游戏是否进行
    bool QGame::isRuning() const
    {
    	return true;
    }
    //退出游戏
    void QGame::quit()
    {
    	m_isRunning = false;
    }
    //运行游戏
    void QGame::runGame()
    {	
    	//显示窗口
    	show();
    	//连接定时器
    	m_mainLoopTimer->callOnTimeout([=]()
    		{
    			//如果游戏没有在运行,那么就结束游戏
    			if (!isRuning())
    			{
    				m_mainLoopTimer->stop();
    				qApp->quit();
    			}
    			//更新游戏,QGame的自定义update
    			update(0);
    			//重绘,父类的update
    			QWidget::update();
    			
    			//测试游戏是否在进行
    			qDebug() << "游戏运行中";
    		}
    	);
    	//开始定时器,设置游戏开启的帧数
    	m_mainLoopTimer->start(m_fps);
    }
    //设置游戏帧率
    void QGame::setFps(qreal fps)
    {
    	m_fps = fps;
    }
    
    //绘图事件
    void QGame::paintEvent(QPaintEvent* ev)
    {
    	//开启画家
    	QPainter painter(this);
    	//渲染游戏
    	render(&painter);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    mian.cpp

    #include 
    #include "QGame.h"
    
    int main(int argc,char* argv[])
    {
    	QApplication a(argc, argv);
    	QGame game;
    	game.init({ 600,600 }, "小瓜");
    	game.runGame();
    	return a.exec();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 运行结果,游戏是在主循环中,基本框架搭建完毕
      在这里插入图片描述

    构建精灵与实体类

    实体类

    • 新建一个Entity空类,什么都不需要继承,这个类里面可以存放各种实体,我们统一称为精灵,每一个精灵都会有一种状态,例如是否死亡;所以我们还需要存在一个类别用与判断实体类型。
    private:
    	bool m_active = true;//实体是否是活动的
    	int m_type = 0;//实体类型
    
    • 1
    • 2
    • 3
    • 那么这个实体被精灵继承的时候,是需要更新释放渲染实体的,所以这个实体类一定要有虚析构与纯虚方法,不然子类可能释放不了造成内存泄漏
    public:
    	virtual ~Entity() {};//虚析构,当子类继承重写的时候就可以释放子类的内存
    	virtual void update() = 0;//更新实体
    	virtual void render(QPainter* painter);//渲染实体
    
    • 1
    • 2
    • 3
    • 4
    • 我们当前实体类中的方法可以设置状态的销毁与实体的类型,到时候由一个统一的管理类去进行管理
    	//接口
    	bool active()const { return m_active; }
    	int type()const { return m_type; }
    
    	//状态销毁
    	void destory() { m_active = false; }
    
    	//设置实体类型
    	void setType(int type) { m_type = type; }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    Entity.h
    #ifndef ENTITY_H_
    #define ENTITY_H_
    
    #include 
    
    class Entity
    {
    public:
    	virtual ~Entity() {};//虚析构,当子类继承重写的时候就可以释放子类的内存
    	virtual void update() = 0;//更新实体
    	virtual void render(QPainter* painter) = 0;//渲染实体
    
    	//接口
    	bool active()const { return m_active; }
    	int type()const { return m_type; }
    
    	//状态销毁
    	void destroy() { m_active = false; }
    
    	//设置实体类型
    	void setType(int type) { m_type = type; }
    	
    private:
    	bool m_active = true;//精灵是否是活动的
    	int m_type = 0;//精灵类型
    
    };
    #endif // !ENTITY_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    精灵类

    • 新建一个精灵类,这个类需要重写Entity的纯虚方法,这个类拥有设置坐标与加载图片的方法
    private:
    	QPixmap m_image;
    	QVector2D m_pos;
    
    • 1
    • 2
    • 3
    • 设置图片
    //设置图片
    void setPixmap(const QString& fileName, const QSize& size = QSize());
    
    • 1
    • 2
    • 设置坐标
    //设置坐标
    void setPos(float x, float y)
    {
    	m_pos = { x,y };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    Sprite.h
    #ifndef SPRITE_H_
    #define SPRITE_H_
    
    #include "Entity.h"
    #include 
    
    class Sprite :public Entity
    {
    public:
    	Sprite() = default;
    	Sprite(const QString& fileName, const QSize& size = QSize());
    	
    	//接口
    	QVector2D getPos()const { return m_pos; }
    	QPixmap getPixmap()const { return m_image; }
    
    	//设置坐标
    	void setPos(float x, float y)
    	{
    		m_pos = { x,y };
    	}
    	//设置图片
    	void setPixmap(const QString& fileName, const QSize& size = QSize());
    	
    	// 通过 Entity 继承
    	void update() override;
    	// 通过 Entity 继承
    	void render(QPainter* painter) override;
    
    
    private:
    	QPixmap m_image;
    	QVector2D m_pos;
    };
    #endif // !SPRITE_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    Sprite.cpp
    #include "Sprite.h"
    
    Sprite::Sprite(const QString& fileName, const QSize& size)
    {
    	setPixmap(fileName, size);
    }
    //设置图片
    void Sprite::setPixmap(const QString& fileName, const QSize& size)
    {
    	m_image.load(fileName);
    	if (size.isValid())
    	{
    		//保持缩放	
    		m_image.scaled(size, Qt::AspectRatioMode::KeepAspectRatio);
    	}
    }
    
    void Sprite::update()
    {
    }
    
    void Sprite::render(QPainter* painter)
    {
    	painter->drawPixmap(m_pos.toPoint(), m_image);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    QGame.cpp
    • 在QGame.cpp中声明一个全局的精灵类,然后去初始化精灵
    #include "QGame.h"
    #include "Sprite.h"
    #include 
    #include 
    
    QGame::QGame(QWidget* parent) :QWidget(parent)
    	, m_mainLoopTimer(new QTimer)
    {
    
    }
    QGame::~QGame()
    {
    	//清理
    	clean();
    }
    
    //全局精灵类,注意这里必须使用指针类,如果是普通类对象会报错
    //因为QT里面QApplication执行后是不允许对象还没有构造完的,指针是个不完整类就不会默认构造
    Sprite* player;
    
    //游戏初始化
    void QGame::init(const QSize& size, const QString& title)
    {
    	//设置窗口固定大小
    	setFixedSize(size);
    	//设置标题
    	setWindowTitle(title);
    
    	player = new Sprite;
    	player->setPixmap(":/plane/Resource/images/hero1.png");
    	//因为上面会有各种资源的初始化,如果初始化失败,那么m_isRunning=false;游戏不继续运行了
    	//初始化成功就为true 
    	m_isRunning = true;
    }
    //游戏反初始化
    void QGame::clean()
    {
    }
    //更新游戏
    void QGame::update(int)
    {
    	player->update();
    }
    //渲染游戏
    void QGame::render(QPainter* painter)
    {
    	player->render(painter);
    }
    //游戏是否进行
    bool QGame::isRuning() const
    {
    	return true;
    }
    //退出游戏
    void QGame::quit()
    {
    	m_isRunning = false;
    }
    //运行游戏
    void QGame::runGame()
    {	
    	//显示窗口
    	show();
    	//连接定时器
    	m_mainLoopTimer->callOnTimeout([=]()
    		{
    			//如果游戏没有在运行,那么就结束游戏
    			if (!isRuning())
    			{
    				m_mainLoopTimer->stop();
    				qApp->quit();
    			}
    			//更新游戏,QGame的自定义update
    			update(0);
    			//重绘,父类的update
    			QWidget::update();
    			
    			//测试游戏是否在进行
    			qDebug() << "游戏运行中";
    		}
    	);
    	//开始定时器,设置游戏开启的帧数
    	m_mainLoopTimer->start(m_fps);
    }
    //设置游戏帧率
    void QGame::setFps(qreal fps)
    {
    	m_fps = fps;
    }
    
    //绘图事件
    void QGame::paintEvent(QPaintEvent* ev)
    {
    	//开启画家
    	QPainter painter(this);
    	//渲染游戏
    	render(&painter);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 运行结果
      在这里插入图片描述

    精灵移动

    • 毫无疑问,我们需要让精灵动起来,那肯定得使用事件去调用,采用两种方式去移动精灵,键盘事件和鼠标事件
    • 使用键盘事件的时候,我们需要知道一个知识点,我们采用分量概念去乘上速度来达到效果
    Sprite.h中
    public:
    	
    Sprite() = default;
    Sprite(const QString& fileName, const QSize& size = QSize());
    	
    //接口
    QVector2D getPos()const { return m_pos; }
    QPixmap getPixmap()const { return m_image; }
    QVector2D velocity() const{ return m_velocity; }
    QVector2D& velocity() { return m_velocity; }
    private//移动速度
    float m_speed = 3;
    //速度分量
    QVector2D m_velocity;
    -----------------------------------------------------------------
    Sprite.cpp中
    void Sprite::update()
    {
    	//获取一下坐标
    	float x = m_pos.x();
    	float y = m_pos.y();
    	//通过分量去改变坐标的速度,移动就比较正常一点
    	x += m_velocity.x() * m_speed;
    	y += m_velocity.y() * m_speed;
    	//将坐标给m_pos
    	m_pos = { x,y };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    QGame.h

    protected:
    	void paintEvent(QPaintEvent* ev) override;
    	void keyPressEvent(QKeyEvent* ev) override;
    	void keyReleaseEvent(QKeyEvent* ev) override;
    	void mouseMoveEvent(QMouseEvent* ev) override;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    QGame.cpp

    //捕获按键事件
    void QGame::keyPressEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    		player->velocity().setY(-1);
    		break;
    	case Qt::Key_Down:
    		player->velocity().setY(1);
    		break;
    	case Qt::Key_Left:
    		player->velocity().setX(-1);
    		break;
    	case Qt::Key_Right:
    		player->velocity().setX(1);
    		break;
    	}
    }
    
    void QGame::keyReleaseEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    	case Qt::Key_Down:
    		player->velocity().setY(0);
    		break;
    	case Qt::Key_Left:
    	case Qt::Key_Right:
    		player->velocity().setX(0);
    		break;
    	}
    }
    
    void QGame::mouseMoveEvent(QMouseEvent* ev)
    {
    	//让鼠标居中到图片中间
    	auto pos = player->sizeImage() / 2;
    	player->setPos(ev->pos() - QPoint{ pos.width(),pos.height() });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    子弹类,飞机类与单例设计模式

    • 构造两个类,一个子弹类一个飞机类,为了在这些类里面能使用QGame的实例,我们设计一个单例设计模式让QGame实例唯一存在

    QGame.h

    #ifndef QGAME_H_
    #define QGAME_H_
    #include 
    #include 
    //宏定义一下这个单例
    #define qGame QGame::instance()
    
    class QGame :public QWidget
    {
    	Q_OBJECT
    public:
    	//单例设计模式,让QGame实例只允许存在一个
    	static QGame* instance();
    
    	QGame(QWidget* parent = nullptr);
    	~QGame();
    	//游戏初始化
    	void init(const QSize& size, const QString& title);
    	//游戏反初始化
    	void clean();
    	//更新游戏,站位符与父类的方法做区别
    	void update(int);
    	//渲染游戏
    	void render(QPainter* painter);
    	//游戏是否进行
    	bool isRuning() const;
    	//退出游戏
    	void quit();
    	//运行游戏
    	void runGame();
    	//设置游戏帧率
    	void setFps(qreal fps);
    
    	//获取游戏帧数的接口
    	qreal fps() const { return m_fps; }
    protected:
    	void paintEvent(QPaintEvent* ev) override;
    	void keyPressEvent(QKeyEvent* ev) override;
    	void keyReleaseEvent(QKeyEvent* ev) override;
    	void mouseMoveEvent(QMouseEvent* ev) override;
    private:
    	//控制游戏进行的变量
    	bool m_isRunning = false;
    	//游戏的主循环
    	QTimer* m_mainLoopTimer{};
    	//游戏帧率60帧
    	qreal m_fps = 1000 / 60;
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    QGame.cpp

    #include "QGame.h"
    #include "Sprite.h"
    #include 
    #include 
    #include 
    #include 
    
    //定义一个静态指针,指向唯一对象
    static QGame* ins = nullptr;
    QGame* QGame::instance()
    {
    	return ins;
    }
    
    QGame::QGame(QWidget* parent) :QWidget(parent)
    	, m_mainLoopTimer(new QTimer)
    {
    	//保证不存在调用多个QGame实例
    	Q_ASSERT_X(ins == nullptr, "QGame", "已经存在一个QGame实例");
    	ins = this;
    }
    QGame::~QGame()
    {
    	//清理
    	clean();
    }
    
    //全局精灵类,注意这里必须使用指针类,如果是普通类对象会报错
    //因为QT里面QApplication执行后是不允许对象还没有构造完的,指针是个不完整类就不会默认构造
    Sprite* player;
    
    //游戏初始化
    void QGame::init(const QSize& size, const QString& title)
    {
    	//设置窗口固定大小
    	setFixedSize(size);
    	//设置标题
    	setWindowTitle(title);
    	//开启鼠标自动追踪
    	setMouseTracking(true);
    
    	player = new Sprite;
    	player->setPixmap(":/plane/Resource/images/hero1.png");
    	//因为上面会有各种资源的初始化,如果初始化失败,那么m_isRunning=false;游戏不继续运行了
    	//初始化成功就为true 
    	m_isRunning = true;
    }
    //游戏反初始化
    void QGame::clean()
    {
    }
    //更新游戏
    void QGame::update(int)
    {
    	player->update();
    }
    //渲染游戏
    void QGame::render(QPainter* painter)
    {
    	player->render(painter);
    }
    //游戏是否进行
    bool QGame::isRuning() const
    {
    	return true;
    }
    //退出游戏
    void QGame::quit()
    {
    	m_isRunning = false;
    }
    //运行游戏
    void QGame::runGame()
    {	
    	//显示窗口
    	show();
    	//连接定时器
    	m_mainLoopTimer->callOnTimeout([=]()
    		{
    			//如果游戏没有在运行,那么就结束游戏
    			if (!isRuning())
    			{
    				m_mainLoopTimer->stop();
    				qApp->quit();
    			}
    			//更新游戏,QGame的自定义update
    			update(0);
    			//重绘,父类的update
    			QWidget::update();
    			
    			//测试游戏是否在进行
    			qDebug() << "游戏运行中";
    		}
    	);
    	//开始定时器,设置游戏开启的帧数
    	m_mainLoopTimer->start(m_fps);
    }
    //设置游戏帧率
    void QGame::setFps(qreal fps)
    {
    	m_fps = fps;
    }
    
    //绘图事件
    void QGame::paintEvent(QPaintEvent* ev)
    {
    	//开启画家
    	QPainter painter(this);
    	//渲染游戏
    	render(&painter);
    }
    
    //捕获按键事件
    void QGame::keyPressEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    		player->velocity().setY(-1);
    		break;
    	case Qt::Key_Down:
    		player->velocity().setY(1);
    		break;
    	case Qt::Key_Left:
    		player->velocity().setX(-1);
    		break;
    	case Qt::Key_Right:
    		player->velocity().setX(1);
    		break;
    	}
    }
    
    void QGame::keyReleaseEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    	case Qt::Key_Down:
    		player->velocity().setY(0);
    		break;
    	case Qt::Key_Left:
    	case Qt::Key_Right:
    		player->velocity().setX(0);
    		break;
    	}
    }
    
    void QGame::mouseMoveEvent(QMouseEvent* ev)
    {
    	//让鼠标居中到图片中间
    	auto pos = player->sizeImage() / 2;
    	player->setPos(ev->pos() - QPoint{ pos.width(),pos.height() });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153

    PlayerPlane.h

    #ifndef PLAYERPLANE_H_
    #define PLAYERPLANE_H_
    
    #include "Sprite.h"
    #include "Bullet.h"
    #include 
    class PlayerPlane : public Sprite
    {
    public:
    	PlayerPlane();
    	//发射子弹
    	bool emitBullet();
    private:
    
    };
    #endif // !PLAYERPLANE_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • PlayerPlane.cpp
    #include "PlayerPlane.h"
    
    PlayerPlane::PlayerPlane()
    {
    	
    }
    
    bool PlayerPlane::emitBullet()
    {
    	return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Bullte.h

    #ifndef BULLET_H_
    #define BULLET_H_
    
    #include "Sprite.h"
    class Bullet :public Sprite
    {
    public:
    	//更新子弹出边界就得消失
    	void update() override;
    private:
    
    };
    #endif // !BULLET_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Bullte.cpp

    #include "Bullet.h"
    #include "QGame.h"
    void Bullet::update()
    {
    	//父类方法帮忙移动
    	Sprite::update();
    	//就可以调用游戏唯一的单例
    	//如果子弹超出边界让子弹消失
    	if (getPos().x() > qGame->width() || getPos().x() < 0 - sizeImage().width() ||
    		getPos().y() > qGame->height() || getPos().y() < 0 - sizeImage().height())
    	{
    		destroy();
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    精灵管理类

    • 创建一个EntityManager类来管理所有的实体与精灵,为这个类构造单例,然后使用链表去管理存储所有的实体与精灵。主游戏里面的所有实体与精灵就可以通过EntityManger这个单例去完成操作

    EntityManager.h

    #ifndef		ENTITYMANAGER_H_
    #define		ENTITYMANAGER_H_
    
    #include"Sprite.h"
    #include
    #include
    #include
    class EntityManager
    {
    public:
    	//得到唯一的实例
    	static EntityManager& instance()
    	{
    		static EntityManager ev;
    		return ev;
    	}
    	//更新管理的所有实体与精灵
    	void update()
    	{
    		for (auto& e : m_entities)
    		{
    			e->update();
    		}
    	}
    	//渲染
    	void render(QPainter* painter)
    	{
    		for (auto& e : m_entities)
    		{
    			e->render(painter);
    		}
    	}
    	
    	//添加实体开个模版方便使用
    	template<typename T = Entity>
    	T* addEntity(T* e)
    	{
    		m_entities.emplaceBack(e);
    		return e;
    	}
    
    	//刷新,如果有实体要消除,就在这里销毁
    	void refresh()
    	{
    		m_entities.removeIf([](Entity* e)
    			{
    				if (!e->active())
    				{
    					//测试输出
    					qDebug() << "destoryed" << e;
    					//释放这个实体
    					delete e;
    					return true;
    				}
    				return false;
    			});
    		//测试输出
    		qDebug() << m_entities.size();
    	}
    	
    private:
    	QList<Entity*> m_entities;
    	//构造函数私有化,设计单例
    	EntityManager() {}
    };
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    PlayerPlane.h

    #ifndef PLAYERPLANE_H_
    #define PLAYERPLANE_H_
    
    #include "Sprite.h"
    #include "Bullet.h"
    #include 
    class PlayerPlane : public Sprite
    {
    public:
    	//继承父类的构造方法
    	using Sprite::Sprite;
    	PlayerPlane();
    	//发射子弹
    	bool emitBullet();
    private:
    	
    };
    #endif // !PLAYERPLANE_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    此时的PlayerPlane.cpp就可以处理发射子弹了

    #include "PlayerPlane.h"
    #include "EntityManager.h"
    PlayerPlane::PlayerPlane()
    {
    	
    }
    
    bool PlayerPlane::emitBullet()
    {
    	Bullet* b = new Bullet;
    	//添加子弹
    	b->setPixmap(":/plane/Resource/images/bullet2.png");
    	//设置子弹在主角上
    	b->setPos(getPos() + QVector2D{ sizeImage().width() / 2.0f,0.0f });
    	//子弹发出
    	b->velocity().setY(-1);
    	//添加进实体
    	EntityManager::instance().addEntity(b);
        return false;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    QGame.cpp

    #include "QGame.h"
    #include "Sprite.h"
    #include "EntityManager.h"
    #include "PlayerPlane.h"
    
    #include 
    #include 
    #include 
    #include 
    
    //定义一个静态指针,指向唯一对象
    static QGame* ins = nullptr;
    QGame* QGame::instance()
    {
    	return ins;
    }
    
    QGame::QGame(QWidget* parent) :QWidget(parent)
    	, m_mainLoopTimer(new QTimer)
    {
    	//保证不存在调用多个QGame实例
    	Q_ASSERT_X(ins == nullptr, "QGame", "已经存在一个QGame实例");
    	ins = this;
    }
    QGame::~QGame()
    {
    	//清理
    	clean();
    }
    
    //全局精灵类,注意这里必须使用指针类,如果是普通类对象会报错
    //因为QT里面QApplication执行后是不允许对象还没有构造完的,指针是个不完整类就不会默认构造
    PlayerPlane* player;
    
    //游戏初始化
    void QGame::init(const QSize& size, const QString& title)
    {
    	//设置窗口固定大小
    	setFixedSize(size);
    	//设置标题
    	setWindowTitle(title);
    	//开启鼠标自动追踪
    	setMouseTracking(true);
    
    	player = EntityManager::instance().addEntity(new PlayerPlane(":/plane/Resource/images/hero1.png"));
    	
    	//因为上面会有各种资源的初始化,如果初始化失败,那么m_isRunning=false;游戏不继续运行了
    	//初始化成功就为true 
    	m_isRunning = true;
    }
    //游戏反初始化
    void QGame::clean()
    {
    }
    //更新游戏
    void QGame::update(int)
    {
    	//刷新销毁所有不需要的实体精灵
    	EntityManager::instance().refresh();
    	//更新实体精灵信息
    	EntityManager::instance().update();
    	//发射子弹
    	player->emitBullet();
    }
    //渲染游戏
    void QGame::render(QPainter* painter)
    {
    	EntityManager::instance().render(painter);
    }
    //游戏是否进行
    bool QGame::isRuning() const
    {
    	return true;
    }
    //退出游戏
    void QGame::quit()
    {
    	m_isRunning = false;
    }
    //运行游戏
    void QGame::runGame()
    {	
    	//显示窗口
    	show();
    	//连接定时器
    	m_mainLoopTimer->callOnTimeout([=]()
    		{
    			//如果游戏没有在运行,那么就结束游戏
    			if (!isRuning())
    			{
    				m_mainLoopTimer->stop();
    				qApp->quit();
    			}
    
    			//更新游戏,QGame的自定义update
    			update(0);
    
    			//重绘,父类的update
    			QWidget::update();
    			
    			//测试游戏是否在进行
    			//qDebug() << "游戏运行中";
    		}
    	);
    	//开始定时器,设置游戏开启的帧数
    	m_mainLoopTimer->start(m_fps);
    }
    //设置游戏帧率
    void QGame::setFps(qreal fps)
    {
    	m_fps = fps;
    }
    
    //绘图事件
    void QGame::paintEvent(QPaintEvent* ev)
    {
    	//开启画家
    	QPainter painter(this);
    	//渲染游戏
    	render(&painter);
    }
    
    //捕获按键事件
    void QGame::keyPressEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    		player->velocity().setY(-1);
    		break;
    	case Qt::Key_Down:
    		player->velocity().setY(1);
    		break;
    	case Qt::Key_Left:
    		player->velocity().setX(-1);
    		break;
    	case Qt::Key_Right:
    		player->velocity().setX(1);
    		break;
    	}
    }
    
    void QGame::keyReleaseEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    	case Qt::Key_Down:
    		player->velocity().setY(0);
    		break;
    	case Qt::Key_Left:
    	case Qt::Key_Right:
    		player->velocity().setX(0);
    		break;
    	}
    }
    
    void QGame::mouseMoveEvent(QMouseEvent* ev)
    {
    	//让鼠标居中到图片中间
    	auto pos = player->sizeImage() / 2;
    	player->setPos(ev->pos() - QPoint{ pos.width(),pos.height() });
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164

    背景图滚动

    • 思路:因为沿着y轴移动,用两个变量来表示图片不同位置的y坐标,然后一个位置在窗口上,一个位置在窗口上方,让两个变量一直自增实现滚动像素,当窗口上的坐标大于窗口高度时,就把窗口上的y坐标重置为0,当窗口之上的坐标大于0时就把y坐标重置为一开始的窗口之上的坐标
    Map::Map()
    {
    	//加载背景图
    	m_pixmap.load(":/plane/Resource/images/background.png");
    	//一个在窗口上面
    	yPos1 = -m_pixmap.height();
    	//一个在窗口上
    	yPos2 = 0;
    }
    void Map::update()
    {
    	//实现滚动背景
    	yPos1 += m_scrollSpeed;
    	if (yPos1 >= 0)
    	{
    		yPos1 = -m_pixmap.height();
    	}
    	yPos2 += m_scrollSpeed;
    	//大于等于窗口高度后重新置为0
    	if (yPos2 >= qGame->height())
    	{
    		yPos2 = 0;
    	}
    }
    void Map::render(QPainter* painter)
    {
    	painter->drawPixmap(0, yPos1, m_pixmap);
    	painter->drawPixmap(0, yPos2, m_pixmap);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 新建地图类继承实体类,然后去重写渲染与更新方法

    Map.h

    #ifndef MAP_H_
    #define MAP_H_
    
    #include "Entity.h"
    class Map :public Entity
    {
    public:
    	Map();
    
    	// 通过 Entity 继承
    	virtual void update() override;
    
    	virtual void render(QPainter* painter) override;
    private:
    	QPixmap m_pixmap;
    	//用来实现滚动
    	int yPos1,yPos2;
    	int m_scrollSpeed = 2;
    };
    #endif // !MAP_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Map.cpp

    #include "Map.h"
    #include "QGame.h"
    Map::Map()
    {
    	//加载背景图
    	m_pixmap.load(":/plane/Resource/images/background.png");
    	//一个在窗口上面
    	yPos1 = -m_pixmap.height();
    	//一个在窗口上
    	yPos2 = 0;
    }
    
    void Map::update()
    {
    	//实现滚动背景
    	yPos1 += m_scrollSpeed;
    	if (yPos1 >= 0)
    	{
    		yPos1 = -m_pixmap.height();
    	}
    	yPos2 += m_scrollSpeed;
    	//大于等于窗口高度后重新置为0
    	if (yPos2 >= qGame->height())
    	{
    		yPos2 = 0;
    	}
    }
    
    void Map::render(QPainter* painter)
    {
    	painter->drawPixmap(0, yPos1, m_pixmap);
    	painter->drawPixmap(0, yPos2, m_pixmap);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    子弹与敌机碰撞

    • 新建一个类用来存放应该enum,enum里面存放不同类别标识,现在就需要在子弹,player,敌机生成的时候设置类别,方便后面进行碰撞判断,在EntityManager中提供类别识别方法,注意识别类型要是活动的,不然就没意义,在Sprite中构造矩阵变量,在update方法中添加矩阵的构造,采用矩阵碰撞方式去检测碰撞,最后在QGame.cpp中去完成敌机的生成与碰撞。
    • 基本完整框架如下:

    main.cpp

    #include 
    #include "QGame.h"
    
    int main(int argc,char* argv[])
    {
    	QApplication a(argc, argv);
    	QGame game;
    	game.init({ 480,852 }, "小瓜");
    	game.runGame();
    
    	return a.exec();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    QGame.h

    #ifndef QGAME_H_
    #define QGAME_H_
    #include 
    #include 
    //宏定义一下这个单例
    #define qGame QGame::instance()
    
    class QGame :public QWidget
    {
    	Q_OBJECT
    public:
    	//单例设计模式,让QGame实例只允许存在一个
    	static QGame* instance();
    
    	QGame(QWidget* parent = nullptr);
    	~QGame();
    	//游戏初始化
    	void init(const QSize& size, const QString& title);
    	//游戏反初始化
    	void clean();
    	//更新游戏,站位符与父类的方法做区别
    	void update(int);
    	//渲染游戏
    	void render(QPainter* painter);
    	//游戏是否进行
    	bool isRuning() const;
    	//退出游戏
    	void quit();
    	//运行游戏
    	void runGame();
    	//设置游戏帧率
    	void setFps(qreal fps);
    
    	//获取游戏帧数的接口
    	qreal fps() const { return m_fps; }
    protected:
    	void paintEvent(QPaintEvent* ev) override;
    	void keyPressEvent(QKeyEvent* ev) override;
    	void keyReleaseEvent(QKeyEvent* ev) override;
    	void mouseMoveEvent(QMouseEvent* ev) override;
    private:
    	//控制游戏进行的变量
    	bool m_isRunning = false;
    	//游戏的主循环
    	QTimer* m_mainLoopTimer{};
    	//游戏帧率60帧
    	qreal m_fps = 1000 / 60;
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    QGame.cpp

    #include "QGame.h"
    #include "Sprite.h"
    #include "EntityManager.h"
    #include "PlayerPlane.h"
    #include "Map.h"
    
    #include 
    #include 
    #include 
    #include 
    #include 
    //随机数头文件
    #include 
    
    //宏定义这个随机生成器函数
    #define qRand(min,max) QRandomGenerator::global()->bounded(min, max)
    
    //定义一个静态指针,指向唯一对象
    static QGame* ins = nullptr;
    QGame* QGame::instance()
    {
    	return ins;
    }
    
    QGame::QGame(QWidget* parent) :QWidget(parent)
    	, m_mainLoopTimer(new QTimer)
    {
    	//保证不存在调用多个QGame实例
    	Q_ASSERT_X(ins == nullptr, "QGame", "已经存在一个QGame实例");
    	ins = this;
    }
    QGame::~QGame()
    {
    	//清理
    	clean();
    }
    
    //全局精灵类,注意这里必须使用指针类,如果是普通类对象会报错
    //因为QT里面QApplication执行后是不允许对象还没有构造完的,指针是个不完整类就不会默认构造
    PlayerPlane* player;
    
    //游戏初始化
    void QGame::init(const QSize& size, const QString& title)
    {
    	//设置窗口固定大小
    	setFixedSize(size);
    	//设置标题
    	setWindowTitle(title);
    	//开启鼠标自动追踪
    	setMouseTracking(true);
    
    	//初始化背景
    	EntityManager::instance().addEntity(new Map);
    	//初始化主角对象
    	player = EntityManager::instance().addEntity(new PlayerPlane(":/plane/Resource/images/hero1.png"));
    	player->setType(Player);
    
    	//因为上面会有各种资源的初始化,如果初始化失败,那么m_isRunning=false;游戏不继续运行了
    	//初始化成功就为true 
    	m_isRunning = true;
    }
    //游戏反初始化
    void QGame::clean()
    {
    }
    //更新游戏
    void QGame::update(int)
    {
    	//刷新销毁所有不需要的实体精灵
    	EntityManager::instance().refresh();
    	//更新实体精灵信息
    	EntityManager::instance().update();
    
    	
    	static int BulletVelocity = 0;
    	//发射子弹的速率
    	if (BulletVelocity % 10 == 0)
    	{
    		//发射子弹
    		player->emitBullet();
    	}
    	//出现敌机
    	if (BulletVelocity % 60 == 0)
    	{
    		QStringList efile = { ":/plane/Resource/images/enemy1.png",":/plane/Resource/images/enemy2.png" };
    		auto enemy = new Sprite(efile[qRand(0,2)]);
    		//设置敌机从上面往下
    		enemy->velocity().setY(1);
    		//设置随机出现的敌机位置
    		enemy->setPos(qRand(0, width()), -50);
    		//设置类别
    		enemy->setType(Enemy);
    		//添加到精灵管理类中
    		EntityManager::instance().addEntity(enemy);
    	}
    
    	//获取子弹列表
    	auto bullet_list = EntityManager::instance().getSpriteByType(bullet);
    	//获取敌机列表
    	auto enemy_list = EntityManager::instance().getSpriteByType(Enemy);
    
    	//遍历,查看是否矩形碰撞
    	for (auto& e : enemy_list)
    	{
    		for (auto& b : bullet_list)
    		{
    			//判断敌机是否包含子弹,如果包含就释放掉子弹和敌机
    			if (e->collider().intersects(b->collider()))
    			{
    				e->destroy();
    				b->destroy();
    				break;
    			}
    		}
    	}
    	BulletVelocity++;
    	qDebug() <<"时间值:" << BulletVelocity;
    }
    //渲染游戏
    void QGame::render(QPainter* painter)
    {
    	EntityManager::instance().render(painter);
    }
    //游戏是否进行
    bool QGame::isRuning() const
    {
    	return true;
    }
    //退出游戏
    void QGame::quit()
    {
    	m_isRunning = false;
    }
    //运行游戏
    void QGame::runGame()
    {	
    	//显示窗口
    	show();
    	//连接定时器
    	m_mainLoopTimer->callOnTimeout([=]()
    		{
    			//如果游戏没有在运行,那么就结束游戏
    			if (!isRuning())
    			{
    				m_mainLoopTimer->stop();
    				qApp->quit();
    			}
    
    			//更新游戏,QGame的自定义update
    			update(0);
    
    			//重绘,父类的update
    			QWidget::update();
    			
    			//测试游戏是否在进行
    			//qDebug() << "游戏运行中";
    		}
    	);
    	//开始定时器,设置游戏开启的帧数
    	m_mainLoopTimer->start(m_fps);
    }
    //设置游戏帧率
    void QGame::setFps(qreal fps)
    {
    	m_fps = fps;
    }
    
    //绘图事件
    void QGame::paintEvent(QPaintEvent* ev)
    {
    	//开启画家
    	QPainter painter(this);
    	//渲染游戏
    	render(&painter);
    }
    
    //捕获按键事件
    void QGame::keyPressEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    		player->velocity().setY(-1);
    		break;
    	case Qt::Key_Down:
    		player->velocity().setY(1);
    		break;
    	case Qt::Key_Left:
    		player->velocity().setX(-1);
    		break;
    	case Qt::Key_Right:
    		player->velocity().setX(1);
    		break;
    	}
    }
    
    void QGame::keyReleaseEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    	case Qt::Key_Down:
    		player->velocity().setY(0);
    		break;
    	case Qt::Key_Left:
    	case Qt::Key_Right:
    		player->velocity().setX(0);
    		break;
    	}
    }
    
    void QGame::mouseMoveEvent(QMouseEvent* ev)
    {
    	//让鼠标居中到图片中间
    	auto pos = player->sizeImage() / 2;
    	player->setPos(ev->pos() - QPoint{ pos.width(),pos.height() });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217

    Entity.h

    #ifndef ENTITY_H_
    #define ENTITY_H_
    
    #include "Global.h"
    #include 
    
    class Entity
    {
    public:
    	virtual ~Entity() {};//虚析构,当子类继承重写的时候就可以释放子类的内存
    	virtual void update() = 0;//更新实体
    	virtual void render(QPainter* painter) = 0;//渲染实体
    
    	//接口
    	bool active()const { return m_active; }
    	int type()const { return m_type; }
    
    	//状态销毁
    	void destroy() { m_active = false; }
    
    	//设置实体类型
    	void setType(int type) { m_type = type; }
    private:
    	
    	bool m_active = true;//实体是否是活动的
    	int m_type = 0;//实体类型
    
    };
    #endif // !ENTITY_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    Sprite.h

    #ifndef SPRITE_H_
    #define SPRITE_H_
    
    #include "Entity.h"
    #include 
    
    class Sprite :public Entity
    {
    public:
    	
    	Sprite() = default;
    	Sprite(const QString& fileName, const QSize& size = QSize());
    	
    	//接口
    	QVector2D getPos()const { return m_pos; }
    	QPixmap getPixmap()const { return m_image; }
    	QVector2D velocity() const{ return m_velocity; }
    	QVector2D& velocity() { return m_velocity; }
    	QRect collider()const { return m_collider; }
    
    	//设置坐标
    	void setPos(float x, float y)
    	{
    		m_pos = { x,y };
    	}
    	void setPos(const QPointF& pos)
    	{
    		m_pos = { (float)pos.x(),(float)pos.y() };
    	}
    	void setPos(const QVector2D& pos) 
    	{
    		m_pos = pos; 
    	}
    
    	//设置速度分量
    	void setVelocity(float vx, float vy)
    	{
    		m_velocity = { vx,vy };
    	}
    	//获取图片大小
    	QSize sizeImage()const;
    	//设置图片
    	void setPixmap(const QString& fileName, const QSize& size = QSize());
    	
    	// 通过 Entity 继承
    	void update() override;
    	// 通过 Entity 继承
    	void render(QPainter* painter) override;
    
    
    private:
    	QPixmap m_image;
    	QVector2D m_pos;
    
    	//移动速度
    	float m_speed = 3;
    	//速度分量
    	QVector2D m_velocity;
    	//碰撞器
    	QRect m_collider{};
    };
    #endif // !SPRITE_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    Sprite.cpp

    #include "Sprite.h"
    
    Sprite::Sprite(const QString& fileName, const QSize& size)
    {
    	setPixmap(fileName, size);
    }
    
    //获取精灵图片大小
    QSize Sprite::sizeImage() const
    {
    	if (m_image.isNull())
    	{
    		return QSize();
    	}
    	return m_image.size();
    }
    //设置图片
    void Sprite::setPixmap(const QString& fileName, const QSize& size)
    {
    	m_image.load(fileName);
    	if (size.isValid())
    	{
    		//保持缩放	
    		m_image.scaled(size, Qt::AspectRatioMode::KeepAspectRatio);
    	}
    }
    
    void Sprite::update()
    {
    	//获取一下坐标
    	float x = m_pos.x();
    	float y = m_pos.y();
    	//通过分量去改变坐标的速度,移动就比较正常一点
    	x += m_velocity.x() * m_speed;
    	y += m_velocity.y() * m_speed;
    	//将坐标给m_pos
    	m_pos = { x,y };
    
    	//设置矩形
    	m_collider = QRect(m_pos.x(), m_pos.y(), m_image.width(), m_image.height());
    }
    
    void Sprite::render(QPainter* painter)
    {
    	painter->drawPixmap(m_pos.toPoint(), m_image);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    Bullet.h

    #ifndef BULLET_H_
    #define BULLET_H_
    
    #include "Sprite.h"
    class Bullet :public Sprite
    {
    public:
    	//更新子弹出边界就得消失
    	void update() override;
    private:
    	
    };
    #endif // !BULLET_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Bullet.cpp

    #include "Bullet.h"
    #include "QGame.h"
    void Bullet::update()
    {
    	//继承父类的方法帮忙移动
    	Sprite::update();
    	//就可以调用游戏唯一的单例
    	//如果子弹超出边界让子弹消失
    	if (getPos().x() > qGame->width() || getPos().x() < 0 - sizeImage().width() ||
    		getPos().y() > qGame->height() || getPos().y() < 0 - sizeImage().height())
    	{
    		destroy();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    PlayerPlane.h

    #ifndef PLAYERPLANE_H_
    #define PLAYERPLANE_H_
    
    #include "Sprite.h"
    #include "Bullet.h"
    #include 
    class PlayerPlane : public Sprite
    {
    public:
    	//继承父类的构造方法
    	using Sprite::Sprite;
    	PlayerPlane();
    	//发射子弹
    	bool emitBullet();
    private:
    	
    };
    #endif // !PLAYERPLANE_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    PlayerPlane.cpp

    #include "PlayerPlane.h"
    #include "EntityManager.h"
    PlayerPlane::PlayerPlane()
    {
    	
    }
    
    bool PlayerPlane::emitBullet()
    {
    	Bullet* b = new Bullet;
    	//添加子弹
    	b->setPixmap(":/plane/Resource/images/bullet2.png");
    	//设置子弹在主角上
    	b->setPos(getPos() + QVector2D{ sizeImage().width() / 2.0f,0.0f });
    	//子弹发出
    	b->velocity().setY(-2);
    	//设置类型
    	b->setType(bullet);
    	//添加进实体
    	EntityManager::instance().addEntity(b);
        return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    EntityManager.h

    #ifndef		ENTITYMANAGER_H_
    #define		ENTITYMANAGER_H_
    
    #include"Sprite.h"
    #include
    #include
    #include
    class EntityManager
    {
    public:
    	//得到唯一的实例
    	static EntityManager& instance()
    	{
    		static EntityManager ev;
    		return ev;
    	}
    	//更新管理的所有实体与精灵
    	void update()
    	{
    		for (auto& e : m_entities)
    		{
    			e->update();
    		}
    	}
    	//渲染
    	void render(QPainter* painter)
    	{
    		for (auto& e : m_entities)
    		{
    			e->render(painter);
    		}
    	}
    	
    	//添加实体开个模版方便使用
    	template<typename T = Entity>
    	T* addEntity(T* e)
    	{
    		m_entities.emplaceBack(e);
    		return e;
    	}
    
    	//刷新,如果有实体要消除,就在这里销毁
    	void refresh()
    	{
    		m_entities.removeIf([](Entity* e)
    			{
    				//不是活动的就释放
    				if (!e->active())
    				{
    					//测试输出
    					qDebug() << "destoryed" << e;
    					//释放这个实体
    					delete e;
    					return true;
    				}
    				return false;
    			});
    		//测试输出
    		qDebug() << m_entities.size();
    	}
    
    	//获取类别,好做碰撞
    	QList<Sprite*> getSpriteByType(int type)
    	{
    		QList<Sprite*> s;
    		for (auto& e : m_entities)
    		{
    			//要是活动的
    			if(e->type()==type && e->active())
    			{
    				s.append(dynamic_cast<Sprite*>(e));
    			}
    		}
    		return s;
    	}
    private:
    	QList<Entity*> m_entities;
    	//构造函数私有化,设计单例
    	EntityManager() {}
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    Map.h

    #ifndef MAP_H_
    #define MAP_H_
    
    #include "Entity.h"
    class Map :public Entity
    {
    public:
    	Map();
    
    	// 通过 Entity 继承
    	virtual void update() override;
    
    	virtual void render(QPainter* painter) override;
    private:
    	QPixmap m_pixmap;
    	//用来实现滚动
    	int yPos1,yPos2;
    	int m_scrollSpeed = 2;
    };
    #endif // !MAP_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Map.cpp

    #include "Map.h"
    #include "QGame.h"
    Map::Map()
    {
    	//加载背景图
    	m_pixmap.load(":/plane/Resource/images/background.png");
    	//一个在窗口上面
    	yPos1 = -m_pixmap.height();
    	//一个在窗口上
    	yPos2 = 0;
    }
    
    void Map::update()
    {
    	//实现滚动背景
    	yPos1 += m_scrollSpeed;
    	if (yPos1 >= 0)
    	{
    		yPos1 = -m_pixmap.height();
    	}
    	yPos2 += m_scrollSpeed;
    	//大于等于窗口高度后重新置为0
    	if (yPos2 >= qGame->height())
    	{
    		yPos2 = 0;
    	}
    }
    
    void Map::render(QPainter* painter)
    {
    	painter->drawPixmap(0, yPos1, m_pixmap);
    	painter->drawPixmap(0, yPos2, m_pixmap);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    Global.h

    #ifndef GLOBAL_H_
    #define GLOBAL_H_
    
    enum EntityType
    {
    	None,
    	Player,
    	Enemy,
    	bullet
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    运行结果

    在这里插入图片描述

    后续版

    main.cpp

    #include 
    #include "QGame.h"
    #include "successWidget.h"
    
    int main(int argc,char* argv[])
    {
    	QApplication a(argc, argv);
    
    	QGame game;
    	successWidget wid;
    	game.init({ 480,852 }, "小瓜");
    	game.runGame();
    	return a.exec();
    }
    #include "main.moc"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    QGame.h

    #ifndef QGAME_H_
    #define QGAME_H_
    #include "successWidget.h"
    #include 
    #include 
    #include 
    #include 
    
    
    //宏定义一下这个单例
    #define qGame QGame::instance()
    
    class QGame :public QWidget
    {
    	Q_OBJECT
    public:
    	//单例设计模式,让QGame实例只允许存在一个
    	static QGame* instance();
    
    	QGame(QWidget* parent = nullptr);
    	~QGame();
    	//游戏初始化
    	void init(const QSize& size, const QString& title);
    	//游戏反初始化
    	void clean();
    	//更新游戏,站位符与父类的方法做区别
    	void update(int);
    	//渲染游戏
    	void render(QPainter* painter);
    	//游戏是否进行
    	bool isRuning() const;
    	//退出游戏
    	void quit();
    	//运行游戏
    	void runGame();
    	//设置游戏帧率
    	void setFps(qreal fps);
    
    	//获取游戏帧数的接口
    	qreal fps() const { return m_fps; }
    protected:
    	void paintEvent(QPaintEvent* ev) override;
    	void keyPressEvent(QKeyEvent* ev) override;
    	void keyReleaseEvent(QKeyEvent* ev) override;
    	void mouseMoveEvent(QMouseEvent* ev) override;
    private:
    	//控制游戏进行的变量
    	bool m_isRunning = false;
    	//游戏的主循环
    	QTimer* m_mainLoopTimer{};
    	//游戏帧率60帧
    	qreal m_fps = 1000 / 60;
    
    	//成功窗口
    	successWidget* sWidget{};
    
    	//成功钥匙
    	int keySuccess = 0;
    
    	//提示胜利文本
    	QLabel* Label = new QLabel("目标杀敌数:20", this);
    	//目前杀敌数
    	QLabel* curLabel = new QLabel("当前杀敌人数:0", this);
    	//目前杀敌数格式化存储
    	char buff[256]{};
    
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    QGame.cpp

    #include "QGame.h"
    #include "Sprite.h"
    #include "EntityManager.h"
    #include "PlayerPlane.h"
    #include "Map.h"
    
    #include 
    #include 
    #include 
    #include 
    #include 
    
    //随机数头文件
    #include 
    
    //宏定义这个随机生成器函数
    #define qRand(min,max) QRandomGenerator::global()->bounded(min, max)
    
    #define successDate 20
    //定义一个静态指针,指向唯一对象
    static QGame* ins = nullptr;
    QGame* QGame::instance()
    {
    	return ins;
    }
    
    QGame::QGame(QWidget* parent) :QWidget(parent)
    	, m_mainLoopTimer(new QTimer)
    	, sWidget(new successWidget)
    {
    	//保证不存在调用多个QGame实例
    	Q_ASSERT_X(ins == nullptr, "QGame", "已经存在一个QGame实例");
    	ins = this;
    	setWindowIcon(QIcon(":/plane/Resource/images/xiaogua.bmp"));
    }
    QGame::~QGame()
    {
    	qDebug() << "QGame窗口结束";
    	//清理
    	clean();
    }
    
    //全局精灵类,注意这里必须使用指针类,如果是普通类对象会报错
    //因为QT里面QApplication执行后是不允许对象还没有构造完的,指针是个不完整类就不会默认构造
    PlayerPlane* player;
    
    
    
    
    
    //游戏初始化
    void QGame::init(const QSize& size, const QString& title)
    {
    	//设置窗口固定大小
    	setFixedSize(size);
    	//设置标题
    	setWindowTitle(title);
    	//开启鼠标自动追踪
    	setMouseTracking(true);
    
    	//初始化背景
    	EntityManager::instance().addEntity(new Map);
    
    	//设置目标提示文本
    	Label->setFont(QFont("宋体", 18));
    	Label->setGeometry(0, 0, 200, 50);
    	curLabel->setFont(QFont("宋体", 18));
    	curLabel->setGeometry(0, 50, 200, 50);
    
    	//初始化主角对象
    	player = EntityManager::instance().addEntity(new PlayerPlane(":/plane/Resource/images/hero1.png"));
    	player->setType(Player);
    	//设置初始主角位置
    	player->setPos(200, 500);
    	
    	//因为上面会有各种资源的初始化,如果初始化失败,那么m_isRunning=false;游戏不继续运行了
    	//初始化成功就为true 
    	m_isRunning = true;
    }
    //游戏反初始化
    void QGame::clean()
    {
    	system("cls");
    }
    //更新游戏
    void QGame::update(int)
    {
    	
    	//刷新销毁所有不需要的实体精灵
    	EntityManager::instance().refresh();
    	//更新实体精灵信息
    	EntityManager::instance().update();
    
    	
    	static int BulletVelocity = 0;
    	//发射子弹的速率
    	if (BulletVelocity % 10 == 0)
    	{
    		//发射子弹
    		player->emitBullet();
    	}
    	//出现敌机
    	if (BulletVelocity % 40 == 0)
    	{
    		QStringList efile = { ":/plane/Resource/images/enemy1.png",":/plane/Resource/images/enemy2.png" };
    		auto enemy = new Sprite(efile[qRand(0,2)]);
    		//设置敌机从上面往下
    		enemy->velocity().setY(1);
    		//设置随机出现的敌机位置
    		enemy->setPos(qRand(0, width()), -50);
    		//设置类别
    		enemy->setType(Enemy);
    		//添加到精灵管理类中
    		EntityManager::instance().addEntity(enemy);
    	}
    
    	//获取子弹列表
    	auto bullet_list = EntityManager::instance().getSpriteByType(bullet);
    	//获取敌机列表
    	auto enemy_list = EntityManager::instance().getSpriteByType(Enemy);
    
    
    
    	//遍历子弹与敌机,查看是否矩形碰撞
    	for (auto& e : enemy_list)
    	{
    		for (auto& b : bullet_list)
    		{
    			//判断敌机是否包含子弹,如果包含就释放掉子弹和敌机
    			if (e->collider().intersects(b->collider()))
    			{		
    				e->destroy();
    				b->destroy();
    				keySuccess++;
    				//qDebug() << "当前杀敌人数:" << keySuccess;
    				sprintf(buff, "当前杀敌人数:%d", keySuccess);
    				curLabel->setText(buff);
    
    				break;
    			}
    		}
    		//判断主角是否与敌机相撞,撞击则游戏结束
    		if (e->collider().intersects(player->collider()))
    		{
    			//qDebug() << "主角死亡";
    			m_isRunning = false;
    			
    		}
    		
    	}
    
    	//游戏成功,结束游戏
    	if (keySuccess == successDate)
    	{
    		m_isRunning = false;
    	}
    
    	BulletVelocity++;
    
    	//设置临界值
    	if (BulletVelocity > 6000) BulletVelocity = 0;
    	//qDebug() <<"时间值:" << BulletVelocity;
    }
    //渲染游戏
    void QGame::render(QPainter* painter)
    {
    	EntityManager::instance().render(painter);
    }
    //游戏是否进行
    bool QGame::isRuning() const
    {
    	return m_isRunning;
    }
    //退出游戏
    void QGame::quit()
    {
    	m_isRunning = false;
    }
    //运行游戏
    void QGame::runGame()
    {	
    	//显示窗口
    	show();
    	//连接定时器
    	m_mainLoopTimer->callOnTimeout([=]()
    		{
    			//如果游戏没有在运行,那么就结束游戏
    			if (!isRuning())
    			{
    				//停止定时器
    				m_mainLoopTimer->stop();
    				//切换到成功窗口
    				instance()->hide();
    				
    				if (keySuccess == successDate)
    				{
    					
    					clean();
    					sWidget->show();
    					qDebug() << "小瓜:啧啧啧啧,太强了吧,这都被你过了,这场紧张又刺激的打飞机。";
    				}
    				else
    				{
    					clean();
    					qDebug() << "小瓜:你真菜,弱智游戏你都玩不过";
    					system("pause");
    					exit(0);
    					
    				}		
    				//最后才能释放释放主角
    				player->destroy();
    			}
    			
    			//更新游戏,QGame的自定义update
    			update(0);
    
    			//重绘,父类的update
    			QWidget::update();
    			
    			//测试游戏是否在进行
    			//qDebug() << "游戏运行中";
    		}
    	);
    	//开始定时器,设置游戏开启的帧数
    	m_mainLoopTimer->start(m_fps);
    
    }
    //设置游戏帧率
    void QGame::setFps(qreal fps)
    {
    	m_fps = fps;
    }
    
    //绘图事件
    void QGame::paintEvent(QPaintEvent* ev)
    {
    	//开启画家
    	QPainter painter(this);
    	//渲染游戏
    	render(&painter);
    }
    
    //捕获按键事件
    void QGame::keyPressEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    		player->velocity().setY(-1);
    		break;
    	case Qt::Key_Down:
    		player->velocity().setY(1);
    		break;
    	case Qt::Key_Left:
    		player->velocity().setX(-1);
    		break;
    	case Qt::Key_Right:
    		player->velocity().setX(1);
    		break;
    	}
    }
    
    void QGame::keyReleaseEvent(QKeyEvent* ev)
    {
    	switch (ev->key())
    	{
    	case Qt::Key_Up:
    	case Qt::Key_Down:
    		player->velocity().setY(0);
    		break;
    	case Qt::Key_Left:
    	case Qt::Key_Right:
    		player->velocity().setX(0);
    		break;
    	}
    }
    
    void QGame::mouseMoveEvent(QMouseEvent* ev)
    {
    	//让鼠标居中到图片中间
    	auto pos = player->sizeImage() / 2;
    	player->setPos(ev->pos() - QPoint{ pos.width(),pos.height() });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283

    Entity.h

    #ifndef ENTITY_H_
    #define ENTITY_H_
    
    #include "Global.h"
    #include 
    
    class Entity
    {
    public:
    	virtual ~Entity() {};//虚析构,当子类继承重写的时候就可以释放子类的内存
    	virtual void update() = 0;//更新实体
    	virtual void render(QPainter* painter) = 0;//渲染实体
    
    	//接口
    	bool active()const { return m_active; }
    	int type()const { return m_type; }
    
    	//状态销毁
    	void destroy() { m_active = false; }
    
    	//设置实体类型
    	void setType(int type) { m_type = type; }
    private:
    	
    	bool m_active = true;//实体是否是活动的
    	int m_type = 0;//实体类型
    
    };
    #endif // !ENTITY_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    Sprite.h

    #ifndef SPRITE_H_
    #define SPRITE_H_
    
    #include "Entity.h"
    #include 
    
    class Sprite :public Entity
    {
    public:
    	
    	Sprite() = default;
    	Sprite(const QString& fileName, const QSize& size = QSize());
    	
    	//接口
    	QVector2D getPos()const { return m_pos; }
    	QPixmap getPixmap()const { return m_image; }
    	QVector2D velocity() const{ return m_velocity; }
    	QVector2D& velocity() { return m_velocity; }
    	QRect collider()const { return m_collider; }
    
    	//设置坐标
    	void setPos(float x, float y)
    	{
    		m_pos = { x,y };
    	}
    	void setPos(const QPointF& pos)
    	{
    		m_pos = { (float)pos.x(),(float)pos.y() };
    	}
    	void setPos(const QVector2D& pos) 
    	{
    		m_pos = pos; 
    	}
    
    	//设置速度分量
    	void setVelocity(float vx, float vy)
    	{
    		m_velocity = { vx,vy };
    	}
    	//获取图片大小
    	QSize sizeImage()const;
    	//设置图片
    	void setPixmap(const QString& fileName, const QSize& size = QSize());
    	
    	// 通过 Entity 继承
    	void update() override;
    	// 通过 Entity 继承
    	void render(QPainter* painter) override;
    
    
    private:
    	QPixmap m_image;
    	QVector2D m_pos;
    
    	//移动速度
    	float m_speed = 3;
    	//速度分量
    	QVector2D m_velocity;
    	//碰撞器
    	QRect m_collider{};
    };
    #endif // !SPRITE_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    Sprite.cpp

    #include "Sprite.h"
    
    Sprite::Sprite(const QString& fileName, const QSize& size)
    {
    	setPixmap(fileName, size);
    }
    
    //获取精灵图片大小
    QSize Sprite::sizeImage() const
    {
    	if (m_image.isNull())
    	{
    		return QSize();
    	}
    	return m_image.size();
    }
    //设置图片
    void Sprite::setPixmap(const QString& fileName, const QSize& size)
    {
    	m_image.load(fileName);
    	if (size.isValid())
    	{
    		//保持缩放	
    		m_image.scaled(size, Qt::AspectRatioMode::KeepAspectRatio);
    	}
    }
    
    void Sprite::update()
    {
    	//获取一下坐标
    	float x = m_pos.x();
    	float y = m_pos.y();
    	//通过分量去改变坐标的速度,移动就比较正常一点
    	x += m_velocity.x() * m_speed;
    	y += m_velocity.y() * m_speed;
    	//将坐标给m_pos
    	m_pos = { x,y };
    
    	//设置矩形
    	m_collider = QRect(m_pos.x(), m_pos.y(), m_image.width(), m_image.height());
    }
    
    void Sprite::render(QPainter* painter)
    {
    	painter->drawPixmap(m_pos.toPoint(), m_image);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    Bullet.h

    #ifndef BULLET_H_
    #define BULLET_H_
    
    #include "Sprite.h"
    class Bullet :public Sprite
    {
    public:
    	//更新子弹出边界就得消失
    	void update() override;
    private:
    	
    };
    #endif // !BULLET_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Bullet.cpp

    #include "Bullet.h"
    #include "QGame.h"
    void Bullet::update()
    {
    	//继承父类的方法帮忙移动
    	Sprite::update();
    	//就可以调用游戏唯一的单例
    	//如果子弹超出边界让子弹消失
    	if (getPos().x() > qGame->width() || getPos().x() < 0 - sizeImage().width() ||
    		getPos().y() > qGame->height() || getPos().y() < 0 - sizeImage().height())
    	{
    		destroy();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    PlayerPlane.h

    #ifndef PLAYERPLANE_H_
    #define PLAYERPLANE_H_
    
    #include "Sprite.h"
    #include "Bullet.h"
    #include 
    class PlayerPlane : public Sprite
    {
    public:
    	~PlayerPlane();
    
    	//继承父类的构造方法
    	using Sprite::Sprite;
    	PlayerPlane();
    	//发射子弹
    	bool emitBullet();
    private:
    	
    };
    #endif // !PLAYERPLANE_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    PlayerPlane.cpp

    #include "PlayerPlane.h"
    #include "EntityManager.h"
    PlayerPlane::~PlayerPlane()
    {
    	//qDebug() << "主角释放成功";
    }
    PlayerPlane::PlayerPlane()
    {
    
    }
    
    bool PlayerPlane::emitBullet()
    {
    	Bullet* b = new Bullet;
    	//添加子弹
    	b->setPixmap(":/plane/Resource/images/bullet2.png");
    	//设置子弹在主角上
    	b->setPos(getPos() + QVector2D{ sizeImage().width() / 2.0f,0.0f });
    	//子弹发出
    	b->velocity().setY(-2);
    	//设置类型
    	b->setType(bullet);
    	//添加进实体
    	EntityManager::instance().addEntity(b);
        return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    EntityManager.h

    #ifndef		ENTITYMANAGER_H_
    #define		ENTITYMANAGER_H_
    
    #include"Sprite.h"
    #include
    #include
    #include
    class EntityManager
    {
    public:
    	//得到唯一的实例
    	static EntityManager& instance()
    	{
    		static EntityManager ev;
    		return ev;
    	}
    	//更新管理的所有实体与精灵
    	void update()
    	{
    		for (auto& e : m_entities)
    		{
    			e->update();
    		}
    	}
    	//渲染
    	void render(QPainter* painter)
    	{
    		for (auto& e : m_entities)
    		{
    			e->render(painter);
    		}
    	}
    	
    	//添加实体开个模版方便使用
    	template<typename T = Entity>
    	T* addEntity(T* e)
    	{
    		m_entities.emplaceBack(e);
    		return e;
    	}
    
    	//刷新,如果有实体要消除,就在这里销毁
    	void refresh()
    	{
    		m_entities.removeIf([](Entity* e)
    			{
    				//不是活动的就释放
    				if (!e->active())
    				{
    					//测试输出
    					//qDebug() << "destoryed" << e;
    					//释放这个实体
    					delete e;
    					return true;
    				}
    				return false;
    			});
    		//测试输出
    		//qDebug() << "目前子弹总数" << m_entities.size();
    	}
    
    	//获取类别,好做碰撞
    	QList<Sprite*> getSpriteByType(int type)
    	{
    		QList<Sprite*> s;
    		for (auto& e : m_entities)
    		{
    			//要是活动的
    			if(e->type()==type && e->active())
    			{
    				s.append(dynamic_cast<Sprite*>(e));
    			}
    		}
    		return s;
    	}
    private:
    	QList<Entity*> m_entities;
    	//构造函数私有化,设计单例
    	EntityManager() {}
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    Map.h

    #ifndef MAP_H_
    #define MAP_H_
    
    #include "Entity.h"
    #include 
    class Map :public Entity
    {
    public:
    	Map();
    
    	// 通过 Entity 继承
    	virtual void update() override;
    
    	virtual void render(QPainter* painter) override;
    private:
    	QPixmap m_pixmap;
    	//用来实现滚动
    	int yPos1,yPos2;
    	int m_scrollSpeed = 2;
    
    };
    #endif // !MAP_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Map.cpp

    #include "Map.h"
    #include "QGame.h"
    Map::Map()
    {
    	//加载背景图
    	m_pixmap.load(":/plane/Resource/images/background.png");
    	//一个在窗口上面
    	yPos1 = -m_pixmap.height();
    	//一个在窗口上
    	yPos2 = 0;
    }
    
    void Map::update()
    {
    	//实现滚动背景
    	yPos1 += m_scrollSpeed;
    	if (yPos1 >= 0)
    	{
    		yPos1 = -m_pixmap.height();
    	}
    	yPos2 += m_scrollSpeed;
    	//大于等于窗口高度后重新置为0
    	if (yPos2 >= qGame->height())
    	{
    		yPos2 = 0;
    	}
    }
    
    void Map::render(QPainter* painter)
    {
    	
    	painter->drawPixmap(0, yPos1, m_pixmap);
    	painter->drawPixmap(0, yPos2, m_pixmap);
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    Global.h

    #ifndef GLOBAL_H_
    #define GLOBAL_H_
    
    enum EntityType
    {
    	None,
    	Player,
    	Enemy,
    	bullet
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    successWidget.h

    #ifndef SUCCCESSWIDGET_H_
    #define SUCCCESSWIDGET_H_
    #include 
    #include 
    #include 
    #include 
    #include 
    class successWidget :public QWidget
    {
    	Q_OBJECT
    public:
    	successWidget(QWidget* parent = nullptr);
    	~successWidget();
    	
    protected:
    	bool eventFilter(QObject* watched, QEvent* event) override;
    private:
    	QPoint m_pos;
    
    };
    
    
    #endif // !SUCCCESSWIDGET_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    successWidget.cpp

    #include "successWidget.h"
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    successWidget::successWidget(QWidget* parent)
    {
    	{
    		//设置窗口大小
    		setFixedSize(300, 300);
    		//设置窗口为无边框
    		setWindowFlag(Qt::FramelessWindowHint, true);
    		//事件过滤器
    		installEventFilter(this);
    	}	
    	system("cls");
    
    	
    		//布局
    		//Button
    		QPushButton* btn1 = new QPushButton("啊,这是什么?");
    		QPushButton* btn2 = new QPushButton("这很抽象");
    		QPushButton* btn3 = new QPushButton("尊嘟假嘟");
    		QPushButton* login = new QPushButton("登录");
    
    		//设置按钮大小
    		btn1->setFixedWidth(100);
    		btn2->setFixedWidth(100);
    		btn3->setFixedWidth(100);
    
    		//ComboBox
    		QComboBox* love = new QComboBox;
    
    		love->addItem("460");
    		love->addItem("720");
    		love->addItem("1080");
    		love->addItem("2k");
    		love->addItem("4k");
    		love->addItem("8k");
    
    		//Label
    		QLabel* xiaogua = new QLabel;
    		QLabel* cake = new QLabel;
    		QLabel* l1 = new QLabel;
    		QLabel* l2 = new QLabel;
    		QLabel* l3 = new QLabel;
    		QLabel* l4 = new QLabel;
    		QLabel* l5 = new QLabel;
    		QLabel* l6 = new QLabel;
    		QLabel* happly = new QLabel("生日快乐呀!");
    		happly->setFont(QFont("宋体", 18));
    	
    
    
    		xiaogua->setFixedSize(90, 90);
    		xiaogua->setPixmap(QPixmap(":/plane/Resource/images/xiaogua.bmp"));
    		xiaogua->setScaledContents(true);
    
    		cake->setFixedSize(90, 90);
    		cake->setPixmap(QPixmap(":/plane/Resource/images/cake2.png"));
    		cake->setScaledContents(true);
    
    		//设置图片大小
    		l1->setFixedSize(90, 90);
    		l1->setPixmap(QPixmap(":/plane/Resource/images/460.png"));
    		l1->setScaledContents(true);
    
    		l2->setFixedSize(90, 90);
    		l2->setPixmap(QPixmap(":/plane/Resource/images/720.png"));
    		l2->setScaledContents(true);
    
    		l3->setFixedSize(90, 90);
    		l3->setPixmap(QPixmap(":/plane/Resource/images/1080p.png"));
    		l3->setScaledContents(true);
    
    		l4->setFixedSize(90, 90);
    		l4->setPixmap(QPixmap(":/plane/Resource/images/2k.png"));
    		l4->setScaledContents(true);
    
    		l5->setFixedSize(90, 90);
    		l5->setPixmap(QPixmap(":/plane/Resource/images/4k.png"));
    		l5->setScaledContents(true);
    
    		l6->setFixedSize(90, 90);
    		l6->setPixmap(QPixmap());
    		l6->setScaledContents(true);
    
    		QBoxLayout* hlayout = new QBoxLayout(QBoxLayout::Direction::LeftToRight);
    		hlayout->addWidget(xiaogua);
    		hlayout->addWidget(btn1);
    		this->setLayout(hlayout);
    	
    	auto passwordLab = new QLabel("密 码");
    	auto passwordEdit = new QLineEdit;
    	
    	
    
    	connect(btn1, &QPushButton::clicked, this, [=]()
    		{
    			
    			hlayout->addWidget(btn2);
    			btn1->close();
    			qDebug() << "我:啊,这是什么?";
    			qDebug() << "小瓜:我是小瓜,我是无敌的神,在很久很久很久以前,我········································此处省略......字";
    		
    		});
    	connect(btn2, &QPushButton::clicked, this, [=]()
    		{
    			hlayout->addWidget(btn3);
    			qDebug() << "我:这很抽象";
    			qDebug() << "小瓜:先别管这么多了,你想知道心是什么形状吗?问我这个万能的神就能帮你哦,嘿嘿嘿!";
    			btn2->close();
    		});
    	connect(btn3, &QPushButton::clicked, this, [=]()
    		{
    
    			hlayout->addWidget(love);
    			btn3->close();
    			xiaogua->close();
    			hlayout->addWidget(l1);
    			qDebug() << "我:尊嘟假嘟";
    			qDebug() << "小瓜:那当然啦";
    		
    			
    		}
    	);
    	
    	connect(love, &QComboBox::currentTextChanged, this, [=](const QString& text)
    		{
    			if (text == "460")
    			{
    				hlayout->addWidget(l1);
    				l2->hide();
    				l3->hide();
    				l4->hide();
    				l5->hide();
    				l1->show();
    			}
    			else if (text == "720")
    			{
    				hlayout->addWidget(l2);
    				l1->hide();
    				l3->hide();
    				l4->hide();
    				l5->hide();
    				l2->show();
    			}
    			else if (text == "1080")
    			{
    				hlayout->addWidget(l3);
    				l2->hide();
    				l1->hide();
    				l4->hide();
    				l5->hide();
    				l3->show();
    			}
    			else if (text == "2k")
    			{
    				hlayout->addWidget(l4);
    				l2->hide();
    				l3->hide();
    				l1->hide();
    				l5->hide();
    				l4->show();
    			}
    			else if (text == "4k")
    			{
    				hlayout->addWidget(l5);
    				l2->hide();
    				l3->hide();
    				l4->hide();
    				l1->hide();
    				l5->show();
    				qDebug() << "我:这个就很抽象了";
    				qDebug() << "小瓜:这多真实,看看8k";
    			}	
    			else if (text == "8k")
    			{
    				l2->hide();
    				l3->hide();
    				l4->hide();
    				l1->hide();
    				l5->hide();
    				qDebug() << "小瓜:请输入密码查看8k";
    				qDebug() << "我:啊,密码是啥?";
    				qDebug() << "小瓜:啦啦啦,你猜?";
    
    				hlayout->addWidget(passwordLab);
    				passwordEdit->setPlaceholderText("输入密码");
    				hlayout->addWidget(passwordEdit);
    				passwordEdit->setEchoMode(QLineEdit::EchoMode::Password);
    				hlayout->addWidget(login);
    			}
    		});
    
    	connect(login, &QPushButton::clicked, this, [=]()
    		{
    			if (passwordEdit->text() == "1021")
    			{
    				love->hide();
    				passwordLab->hide();
    				passwordEdit->hide();
    				login->hide();
    				hlayout->addWidget(cake);
    		
    				hlayout->addWidget(happly);
    				qDebug() << "小瓜:即使在忙碌的日子里,也要祝自己生日快乐呀!";
    			}
    			else
    			{
    				qDebug() << "小瓜:密码不对哦";
    			}
    			
    		}
    	);
    }
    
    successWidget::~successWidget()
    {
    	qDebug() << "successWidget窗口结束";
    }
    
    bool successWidget::eventFilter(QObject* watched, QEvent* event)
    {
    	QWidget* w = dynamic_cast<QWidget*>(watched);
    	//如果窗口没有边框的话,点击窗口区域就可以移动
    	if (w->windowFlags() & Qt::FramelessWindowHint)
    	{
    		//获取鼠标点击事件
    		QMouseEvent* ev = dynamic_cast<QMouseEvent*>(event);
    		if (event->type() == QEvent::MouseButtonPress && ev->button() == Qt::MouseButton::LeftButton)
    		{
    			m_pos = ev->pos();
    		}
    		else if (event->type() == QEvent::MouseButtonRelease && ev->button() == Qt::MouseButton::LeftButton)
    		{
    			m_pos = { 0,0 };
    		}
    		else if (event->type() == QEvent::MouseMove && ev->buttons() & Qt::LeftButton)
    		{
    			w->move(ev->globalPos() - m_pos);
    		}
    	}
    	return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 运行结果
      在这里插入图片描述
  • 相关阅读:
    linux上安装bluesky的步骤
    爬虫 pandas Linux Flume Pig填空题
    STM32F4X OLED使用
    论文阅读(11) 操纵涡环来提高机动性 (2015)
    “从部署到优化,打造高效会议管理系统“
    Streptavidin-Biotin/Biotin-Streptavidin 生物素修饰/标记/偶联链霉亲和素
    如何在2023年学习React
    postman和Jmeter的区别
    前端 :用HTML , CSS ,JS 做一个秒表
    R语言学习笔记
  • 原文地址:https://blog.csdn.net/qq_44924388/article/details/133897843