1.推箱子小游戏
QT./代表的当前目录位置
项目结构
效果图
GameMap.h
text
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
#ifndef GAMEMAP_H
#define GAMEMAP_H
#include <QObject>
#include <QPainter>
enum MapElement
{
Road,
Wall,
Box,
Point,
InPoint
};
class GameMap : public QObject
{
Q_OBJECT
public:
explicit GameMap(QObject *parent = nullptr);
~GameMap();
bool InitByFile(QString fileName);
void Clear();
void Paint(QPainter* _p,QPoint _Pos);
int mRow;
int mCol;
int** mPArr;//用于开辟二维数组 2D地图元素
signals:
public slots:
};
#endif // GAMEMAP_H
GameMap.cpp
text
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
#include "GameMap.h"
#include <QFile>
#include <QDebug>
GameMap::GameMap(QObject *parent) : QObject(parent)
{
mRow = 0;
mCol = 0;
mPArr = nullptr;
}
GameMap::~GameMap()
{
Clear();
}
void GameMap::Clear()
{
if(mPArr != nullptr)
{
for(int i = 0;i < mRow;i++)
{
delete[] mPArr[i];
}
delete[] mPArr;
}
}
bool GameMap::InitByFile(QString fileName)
{
QFile file(fileName);//创建文件对象
if(!file.open(QIODevice::ReadOnly))
{
return false;//打开失败
}
//读取所有内容
QByteArray arrAll = file.readAll();
arrAll.replace("\r\n","\n");//将 "\r\n" 替换成 “\n”
QList<QByteArray> lineList = arrAll.split('\n');//以“\n” 分割子串
mRow = lineList.size();//确定行
mPArr = new int*[mRow];
for(int i = 0;i < mRow;i++)
{
QList<QByteArray> colList = lineList[i].split(',');
mCol = colList.size();//确定列
mPArr[i] = new int[mCol];//开辟列
for(int j = 0;j < mCol;j++)//遍历列
{
mPArr[i][j] = colList[j].toInt();
}
}
}
void GameMap::Paint(QPainter* _p, QPoint _Pos)
{
for(int i = 0;i <mRow;i++)
{
for(int j = 0;j < mCol;j++)
{
QString imgUrl;
switch (mPArr[i][j])
{
case Road: imgUrl = "://Image/sky.png";break;
case Wall: imgUrl = "://Image/wall.png";break;
case Box: imgUrl = "://Image/case.png";break;
case Point: imgUrl = "://Image/end.png";break;
case InPoint: imgUrl = "://Image/win.png";break;
}
QImage img(imgUrl);
_p->drawImage(QRect(_Pos.x() + j*img.width(),_Pos.y() +i*img.height(),img.width(),img.height()),img);
// _p->drawImage(_Pos,img);
}
}
}
Role.h
text
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
#ifndef ROLE_H
#define ROLE_H
#include <QObject>
#include <QPoint>
#include <QImage>
class Role : public QObject
{
Q_OBJECT
public:
explicit Role(QObject *parent = nullptr);
//对应在地图的映射行列
int mRow;
int mCol;
//画图位置
//人在二维数组里的x,y和显示的x,y坐标轴是反过来的
QPoint mPaintPos;
//人物图片
QImage mImg;
void Move(int _dRow,int _dCol);//移动函数
void Paint(QPainter* _p,QPoint _pos);//自己的绘制函数
signals:
public slots:
};
#endif // ROLE_H
Role.cpp
text
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
#include "Role.h"
#include <QPainter>
Role::Role(QObject *parent) : QObject(parent)
{
mRow = 1;
mCol = 1;
mImg = QImage("://Image/people.png");
//显示位置
mPaintPos = QPoint(mCol,mRow) * mImg.width();
}
void Role::Move(int _dRow,int _dCol)
{
mRow += _dRow;
mCol += _dCol;
mPaintPos = QPoint(mCol,mRow) * mImg.width();
}
void Role::Paint(QPainter* _p,QPoint _pos)
{
_p->drawImage(mPaintPos + _pos,mImg);
}
Widget.h
text
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
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "GameMap.h"
#include "Role.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
virtual void paintEvent(QPaintEvent* event);//绘图事件函数
virtual void keyPressEvent(QKeyEvent* event);//键盘按下事件
void Collision(int _dRow,int _dCol);
~Widget();
private:
Ui::Widget *ui;
GameMap* mPMap;
//画家
QPainter* mMapPainter;
//角色
Role* mRole;
//游戏更新定时器 解决paintEvent不定时更新问题
QTimer* mTimer;
};
#endif // WIDGET_H
Widget.cpp
text
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
#include "Widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QPainter>
#include <QMessageBox>
#include <QKeyEvent>
#include <QtDebug>
#include <QTimer>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//初始化地图元素
mPMap = new GameMap(this);
// QString fileName =QFileDialog::getOpenFileName(this,"打开地图","./","*.txt");
if(!mPMap->InitByFile("./Map/lv1.txt"))
{
QMessageBox::warning(this,"警告","文件打开失败");
}
mMapPainter = new QPainter(this);//创建画家
mRole = new Role(this);
//定时调用更新函数
mTimer = new QTimer(this);
mTimer->start(100);
//定时调用更新函数
connect(mTimer,&QTimer::timeout,[this](){this->update();});
setFixedSize(1024,768);//固定窗口大小
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
mMapPainter->begin(this);//设置画布
//画背景
QPainter bgPainter(this);
bgPainter.drawImage(QRect(0,0,1024,768),QImage("://Image/ground.png"));
//画地图
mPMap->Paint(mMapPainter,QPoint(10,200));
//画人物
mRole->Paint(mMapPainter,QPoint(10,200));
mMapPainter->end();//结束
}
void Widget::keyPressEvent(QKeyEvent* event)
{
switch (event->key())
{
case Qt::Key_W:
case Qt::Key_Up:
{
//逻辑碰撞检测函数
Collision(-1,0);
break;
}
case Qt::Key_S:
case Qt::Key_Down:
{
//逻辑碰撞检测函数
Collision(1,0);
break;
}
case Qt::Key_A:
case Qt::Key_Left:
{
//逻辑碰撞检测函数
Collision(0,-1);
break;
}
case Qt::Key_D:
case Qt::Key_Right:
{
//逻辑碰撞检测函数
Collision(0,1);
break;
}
}
}
void Widget::Collision(int _dRow,int _dCol)
{
//判断位置定义
int newRow = mRole->mRow + _dRow;
int newCol = mRole->mCol + _dCol;
if(mPMap->mPArr[newRow][newCol] == Wall)//判断前方是墙
{
return;
}
else if(mPMap->mPArr[newRow][newCol] == Box)//判断前方是箱子
{
//判断箱子前方
if(mPMap->mPArr[newRow+_dRow][newCol + _dCol] == Road)
{
//改变地图元素
mPMap->mPArr[newRow+_dRow][newCol + _dCol] = Box;//箱子前方变成箱子
mPMap->mPArr[newRow][newCol] = Road;
}
else if(mPMap->mPArr[newRow+_dRow][newCol + _dCol] == Point)
{
//改变地图元素
mPMap->mPArr[newRow+_dRow][newCol + _dCol] = InPoint;//箱子前方变成进目标点
mPMap->mPArr[newRow][newCol] = Road;
}
else
{
return;//无法推动箱子
}
}
else if(mPMap->mPArr[newRow][newCol] == InPoint)//前方是进目标点(箱子与目标重合)
{
//判断目标点前方
if(mPMap->mPArr[newRow+_dRow][newCol + _dCol] == Road)
{
//改变地图元素
mPMap->mPArr[newRow+_dRow][newCol + _dCol] = Box;//目标点前方变成箱子
mPMap->mPArr[newRow][newCol] = Point;
}
else if(mPMap->mPArr[newRow+_dRow][newCol + _dCol] == Point)
{
//改变地图元素
mPMap->mPArr[newRow+_dRow][newCol + _dCol] = InPoint;//箱子进点
mPMap->mPArr[newRow][newCol] = Point;
}
else
{
return;//无法推动箱子
}
}
//否则移动
mRole->Move(_dRow,_dCol);
qDebug() << "人物绘制位置:" << mRole->mPaintPos;
}
2.飞机大战项目实战
2.1注意事项
如何制作合适的图片
p48集12:40开始看 子弹位置的设计
添加音乐注意事项
导入资源
继承体系
2.2目录结构

2.3代码
程序运行结果图
bullet.h
text
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
#ifndef BULLET_H
#define BULLET_H
#include "gameobject.h"
class Bullet : public GameObject
{
public:
//子弹类型
enum BulletType
{
BT_Player,//我方
BT_Enemy//地方
};
explicit Bullet(QObject *parent = nullptr);
Bullet(QPoint _pos,QPixmap _pixmap,int _type);
//移动函数
void BulletMove(QPoint _dir = QPoint(0,-1));
//初始化函数
void Init(QPoint _pos,QPixmap _pixmap);
~Bullet()
{
qDebug() << "子弹被释放";
}
protected:
int mBulletType;
int mSpeed;
QMediaPlayer mMedia;
};
#endif // BULLET_H
bullet.cpp
text
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
#include "bullet.h"
Bullet::Bullet(QObject *parent)
{
mObjectType = GameObject::OT_BulletPlayer;
}
Bullet::Bullet(QPoint _pos, QPixmap _pixmap, int _type)
{
this->setPos(_pos);
this->setPixmap(_pixmap);
this->mBulletType = _type;
mSpeed = 6;
}
void Bullet::BulletMove(QPoint _dir)
{
this->moveBy(_dir.x()*mSpeed,_dir.y()*mSpeed);
}
void Bullet::Init(QPoint _pos, QPixmap _pixmap)
{
this->setPos(_pos);
this->setPixmap(_pixmap);
//----子弹微调整
this->setScale(0.5);//缩放
this->setX(this->x() - this->scale() * pixmap().width()/2);
}
enemy.h
text
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
#ifndef ENEMY_H
#define ENEMY_H
#include "plane.h"
class Enemy : public Plane
{
public:
Enemy()
{
mObjectType = GameObject::OT_Enemy;//标识对象
}
Enemy(QPoint _pos,QPixmap _pixmap);
void EnemyMove(QPoint _dir = QPoint(0,1));
void Init(QPoint _pos,QPixmap _pixmap);
};
#endif // ENEMY_H
enemy.cpp
text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "enemy.h"
Enemy::Enemy(QPoint _pos, QPixmap _pixmap)
{
this->mMoveSpeed = 3;
this->mShootSpeed = 1000;
this->setPos(_pos);
this->setPixmap(_pixmap);
}
void Enemy::EnemyMove(QPoint _dir)
{
this->moveBy(_dir.x()*mMoveSpeed ,_dir.y()*mMoveSpeed);
}
void Enemy::Init(QPoint _pos, QPixmap _pixmap)
{
this->setPos(_pos);
this->setPixmap(_pixmap);
this->mMoveSpeed = 3;
this->mShootSpeed = 1000;
}
enemybullet.h
text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef ENEMYBULLET_H
#define ENEMYBULLET_H
#include "bullet.h"
class EnemyBullet : public Bullet
{
public:
explicit EnemyBullet(QObject *parent = nullptr);
void PlaySound();
};
#endif // ENEMYBULLET_H
-enemybullet.cpp
text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "enemybullet.h"
EnemyBullet::EnemyBullet(QObject *parent)
{
mObjectType = GameObject::OT_EnemyBullet;
}
void EnemyBullet::PlaySound()
{
mMedia.setMedia(QUrl("qrc:/sound/music/weapon_enemy.wav"));
mMedia.play();
}
gamecontrol.h
text
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
#ifndef GAMECONTROL_H
#define GAMECONTROL_H
#include "gamedefine.h"
#include "widget.h"
class GameControl : public QObject
{
//单例
GameControl(QWidget* parent = nullptr);
static GameControl* instance;
public:
//获取单例
static GameControl* Instance()
{
if(instance == nullptr)
{
return instance = new GameControl(Widget::widget);
}
return instance;
}
//析构
~GameControl()
{
qDebug() << "游戏控制释放";
}
//游戏初始化
void GameInit();
void LoadStartScene();//加载开始场景
void LoadGameScene();//游戏场景加载
//游戏逻辑定时器开启 (游戏开始)
void GameStart();
//游戏逻辑定时器关闭 (游戏结束)
void GameOver();
//背景移动
void BGMove();
//飞机移动函数
void PlaneMove();
//我机子弹生成函数
void PlaneBulletShoot();
//敌机生成
void CreateEnemy();
//碰撞检测
void Collision();
QList<int> mKeyList;//按键组合
protected:
QGraphicsView mGameView;//游戏视图
QGraphicsScene mGameScene;//游戏场景
QGraphicsScene mStartScene;//开始游戏场景
//场景元素
QGraphicsPixmapItem mBackGround1;//背景元素
QGraphicsPixmapItem mBackGround2;
Player mPlane;
//定时器
QTimer* mBGMoveTimer;//背景移动
QTimer* mPlaneMoveTimer;//飞机移动
QTimer* mPlaneShootTimer;//飞机发射定时器
QTimer* mBulletMoveTimer;//子弹移动定时器
QTimer* mEnemyCreateTimer;//敌机创建定时器
QTimer* mEnemyMoveTimer;//敌机创建定时器
//容器
QList<Bullet*> mBulletList;//子弹容器
QList<Enemy*> mEnemyList;//敌机容器
//背景音乐
QMediaPlayer* mMediaBG;
};
#endif // GAMECONTROL_H
gamecontrol.cpp
text
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
#include "gamecontrol.h"
#include "gamedefine.h"
#include "gameobjectpool.h"
GameControl* GameControl::instance = nullptr;
GameControl::GameControl(QWidget* parent) : QObject (parent)
{
// this->setParent(Widget::widget);
}
void GameControl::BGMove()
{
mBackGround2.moveBy(0,2);
mBackGround1.moveBy(0,2);//moveBy 移动量 dx--x方向移动量 dy---y方向移动量
if(mBackGround1.y() >= mBackGround1.pixmap().height())
{
mBackGround1.setY(-mBackGround1.pixmap().height());
}
else if(mBackGround2.y() >= mBackGround2.pixmap().height())
{
mBackGround2.setY(-mBackGround2.pixmap().height());
}
}
void GameControl::GameInit()
{
//对象池初始化
GameObjectPool::Instance()->Init();
//设置视图的父亲为窗口
mGameView.setParent(Widget::widget);
//开始场景初始化
LoadStartScene();
//定时器初始化
mBGMoveTimer = new QTimer(Widget::widget);
mPlaneMoveTimer = new QTimer(Widget::widget);
mPlaneShootTimer = new QTimer(Widget::widget);
mBulletMoveTimer = new QTimer(Widget::widget);
mEnemyCreateTimer = new QTimer(Widget::widget);
mEnemyMoveTimer = new QTimer(Widget::widget);
//绑定定时器处理函数
connect(mBGMoveTimer,&QTimer::timeout,this,&GameControl::BGMove);
connect(mPlaneMoveTimer,&QTimer::timeout,this,&GameControl::PlaneMove);
connect(mPlaneShootTimer,&QTimer::timeout,this,&GameControl::PlaneBulletShoot);
connect(mEnemyCreateTimer,&QTimer::timeout,this,&GameControl::CreateEnemy);
connect(mBulletMoveTimer,&QTimer::timeout,[this](){
//子弹移动
for(auto bullet : mBulletList)
{
bullet->BulletMove();
//判断越界回收子弹
if(bullet->y() < -200)
{
//移除场景 并回收对象
bullet->GameObjectDelete(&mGameScene);
//移除容器
mBulletList.removeOne(bullet);
}
}
//碰撞检测
Collision();
});
connect(mEnemyMoveTimer,&QTimer::timeout,[this](){
for(auto enemy : mEnemyList)
{
enemy->EnemyMove();
if(enemy->y() > GameDefine::ScreenHeight + enemy->pixmap().height())
{
//移除场景 对象池回收对象
enemy->GameObjectDelete(&mGameScene);
//移除容器
mEnemyList.removeOne(enemy);
}
}
});
}
void GameControl::LoadStartScene()
{
mStartScene.setSceneRect(QRect(0,0,512,768));//游戏开始场景大小
mStartScene.addPixmap(QPixmap(":/img/Image/img_bg_logo.jpg"));
auto startBtn = new QToolButton();
startBtn->setAutoRaise(true);
startBtn->setIcon(QIcon(":/img/Image/startBtn.fw.png"));
startBtn->setIconSize(QSize(171,45));
startBtn->move(256-171/2,384);
//开始游戏点击
connect(startBtn,&QToolButton::clicked,[this](){
//加载游戏场景
this->LoadGameScene();
//开始游戏
this->GameStart();
});
mStartScene.addWidget(startBtn);
//设置当前场景为 开始场景
mGameView.setScene(&mStartScene);
mGameView.show();
}
void GameControl::LoadGameScene()
{
mGameScene.setSceneRect(QRect(0,0,512,768));//游戏进行场景大小
mBackGround1.setPixmap(QPixmap(":/img/Image/img_bg_level_2.jpg"));//于QImage用法类似
mBackGround2.setPixmap(QPixmap(":/img/Image/img_bg_level_2.jpg"));
mPlane.setPixmap(QPixmap(":/img/Image/MyPlane1.fw.png"));
//设置元素位置
mBackGround2.setPos(0,-mBackGround2.pixmap().height());
//图片元素添加到场景
mGameScene.addItem(&mBackGround1);
mGameScene.addItem(&mBackGround2);
mGameScene.addItem(&mPlane);
//设置当前场景为游戏场景
mGameView.setScene(&mGameScene);
mGameView.show();
//-------------播放音效
this->mMediaBG = new QMediaPlayer(Widget::widget);
//注意 QUrl路径对象 字符串格式 "qrc:/ + 前缀名+ /路径"
this->mMediaBG->setMedia(QUrl("qrc:/sound/music/starwars.mp3"));
this->mMediaBG->play();
}
void GameControl::GameStart()
{
//开启定时器
mBGMoveTimer->start(GameDefine::BackgroundUpdateTime);
mPlaneMoveTimer->start(GameDefine::PlayerMoveUpdateTime);
mPlaneShootTimer->start(GameDefine::PlaneShootUpdateTime);
mBulletMoveTimer->start(GameDefine::BulletMoveUpdateTime);
mEnemyCreateTimer->start(GameDefine::EnemyCreateTime);
mEnemyMoveTimer->start(GameDefine::EnemyMoveUpdateTime);
}
void GameControl::GameOver()
{
//结束逻辑
//...
}
void GameControl::PlaneMove()
{
for(int keyCode : mKeyList)
{
switch (keyCode)
{
case Qt::Key_W: mPlane.moveBy(0,-1 * mPlane.MoveSpeed());break;
case Qt::Key_S: mPlane.moveBy(0,1*mPlane.MoveSpeed());break;
case Qt::Key_A: mPlane.moveBy(-1*mPlane.MoveSpeed(),0);break;
case Qt::Key_D: mPlane.moveBy(1*mPlane.MoveSpeed(),0);break;
}
}
//*******************************边界判断
if(mPlane.x() < 0)
{
mPlane.setX(0);
}
if(mPlane.y() < 0)
{
mPlane.setY(0);
}
if(mPlane.x() > GameDefine::ScreenWidth - mPlane.pixmap().width())
{
mPlane.setX(GameDefine::ScreenWidth - mPlane.pixmap().width());
}
if(mPlane.y() > GameDefine::ScreenHeight - mPlane.pixmap().height())
{
mPlane.setY(GameDefine::ScreenHeight - mPlane.pixmap().height());
}
}
void GameControl::PlaneBulletShoot()
{
//对象池构建子弹
QPixmap bulletImg(":/img/Image/bulletPlane.fw.png");
QPoint pos(mPlane.x() + mPlane.pixmap().width()/2,mPlane.y());
GameObject* obj = GameObjectPool::Instance()->GetGameObject(GameObject::OT_BulletPlayer);
PlayerBullet* bullet = (PlayerBullet*)obj;
bullet->Init(pos,bulletImg);
bullet->PlaySound();//播放音效
//添加到场景
mGameScene.addItem(bullet);
//添加到子弹管理器
mBulletList.append(bullet);
}
void GameControl::CreateEnemy()
{
// Enemy* enemy = new Enemy(QPoint(randX,-200),pixmap);
//对象池获取对象
QPixmap pixmap(":/img/Image/enemy1.fw.png");
int randX = qrand()%(512-pixmap.width());// [0,32768)
GameObject* obj = GameObjectPool::Instance()->GetGameObject(GameObject::OT_Enemy);
Enemy* enemy = (Enemy*)obj;
enemy->Init(QPoint(randX,-200),pixmap);
//添加到场景
mGameScene.addItem(enemy);
//添加到管理器
mEnemyList.append(enemy);
}
void GameControl::Collision()
{
//遍历子弹
for(int i = 0;i < mBulletList.size();i++)
{
//遍历敌机
for(int j = 0;j < mEnemyList.size();j++)
{
if(mBulletList[i]->collidesWithItem(mEnemyList[j]))//碰撞检测
{
//移除场景 (实际的对象并没有被删除)
// mGameScene.removeItem(mBulletList[i]);
// mGameScene.removeItem(mEnemyList[j]);
//移除场景并回收到对象池
mBulletList[i]->GameObjectDelete(&mGameScene);
mEnemyList[j]->GameObjectDelete(&mGameScene);
//移除管理器
mBulletList.removeOne(mBulletList[i]);
mEnemyList.removeOne(mEnemyList[j]);
}
}
}
}
gamedefine.h
text
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
#ifndef GAMEDEFINE_H
#define GAMEDEFINE_H
#include <QtDebug>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QTimer>
#include <QList>
#include <QToolButton>
#include <QMediaPlayer>
#include "bullet.h"
#include "enemy.h"
#include "player.h"
#include "playerbullet.h"
#include "enemybullet.h"
//游戏定义类 定义游戏相关属性
class GameDefine
{
public:
GameDefine();
static const int PlaneShootUpdateTime = 500;
static const int PlayerMoveUpdateTime = 20;
static const int EnemyMoveUpdateTime = 20;
static const int BulletMoveUpdateTime = 10;
static const int BackgroundUpdateTime = 50;
static const int EnemyCreateTime = 2000;
//屏幕宽高
static const int ScreenWidth = 512;
static const int ScreenHeight = 768;
};
#endif // GAMEDEFINE_H
gamedefine.cpp
text
1
2
3
4
5
6
7
8
#include "gamedefine.h"
GameDefine::GameDefine()
{
}
gameobject.h
text
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
#ifndef GAMEOBJECT_H
#define GAMEOBJECT_H
#include <QGraphicsPixmapItem>
#include <QDebug>
#include <QMediaPlayer>
class GameObject : public QGraphicsPixmapItem
{
public:
enum ObjectType
{
OT_BulletPlayer,
OT_Enemy,
OT_Player,
OT_EnemyBullet
};
explicit GameObject(QObject *parent = nullptr);
int GetType()
{
return mObjectType;
}
//对象回收
void GameObjectDelete(QGraphicsScene* _scene);
//统计对象构造 和析构
static int createCount;
~GameObject()
{
qDebug() << "当前释放第" + QString::number(createCount--) + "个对象";
}
protected:
int mObjectType;
};
#endif // GAMEOBJECT_H
gameobject.cpp`
text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "gameobject.h"
#include <QGraphicsScene>
#include "gameobjectpool.h"
#include <QDebug>
int GameObject::createCount = 0;
GameObject::GameObject(QObject *parent)
{
createCount++;
qDebug() << "当前创建" + QString::number(createCount) + "个对象";
}
void GameObject::GameObjectDelete(QGraphicsScene* _scene)
{
_scene->removeItem(this);//移除场景
GameObjectPool::Instance()->RecoveryGameObject(this);//回收对象
}
gameobjectpool.h
text
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
#ifndef GAMEOBJECTPOOL_H
#define GAMEOBJECTPOOL_H
#include <QObject>
#include "gamedefine.h"
#include "widget.h"
/*
*
* 当对象被频繁创建 或 删除时 大量使用 new 和 delete 关键字
处理堆内存 如果该对象比较大 则对计算机的消耗就越大 很有可以在创建大量
对象时出现卡顿情况 频繁的创建和删除对计算机消耗比较大。
对象缓存池:对象缓存池 预先缓存一定数量的对象 对象需要被
使用时 直接从对象池中获取对象 能有效的解决在创建时导致的卡顿问题。在对象
需要被消耗时 不销毁对象将其回收到对象缓存池中 循环利用从而解决对计算机的
销毁。
(简单来说 就是游戏对象的循环利用)
*/
class GameObjectPool : public QObject
{
GameObjectPool(QObject *parent = nullptr);
static GameObjectPool* instance;
public:
static GameObjectPool* Instance()
{
if(instance == nullptr)
{
return instance = new GameObjectPool(Widget::widget);
}
return instance;
}
//对象池初始化 缓存对象
void Init();
//获取对象
GameObject* GetGameObject(int _objectType);
//对象回收
void RecoveryGameObject(GameObject* _object);
//内存清除
void Clear();
~GameObjectPool();
protected:
//玩家子弹对象池容器
QList<PlayerBullet*> mBulletPool;
//敌机对象池容器
QList<Enemy*> mEnemyPool;
};
#endif // GAMEOBJECTPOOL_H
gameobjectpool.cpp
text
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
#include "gameobjectpool.h"
GameObjectPool* GameObjectPool::instance = nullptr;
GameObjectPool::GameObjectPool(QObject *parent) : QObject(parent)
{
}
void GameObjectPool::Init()
{
//预先生产对象
for (int i = 0;i < 20; i++)
{
//子弹生产
PlayerBullet* bullet = new PlayerBullet();
mBulletPool.append(bullet);
//敌机生产
Enemy* enemy = new Enemy();
mEnemyPool.append(enemy);
}
}
GameObject *GameObjectPool::GetGameObject(int _objectType)
{
switch (_objectType)
{
case GameObject::OT_BulletPlayer://子弹
{
PlayerBullet* bullet = mBulletPool.first();
mBulletPool.pop_front();
return bullet;
}
case GameObject::OT_Enemy://敌机
{
Enemy* enemy = mEnemyPool.first();
mEnemyPool.pop_front();
return enemy;
}
}
}
void GameObjectPool::RecoveryGameObject(GameObject *_object)
{
switch (_object->GetType())
{
case GameObject::OT_BulletPlayer://子弹
{
mBulletPool.append((PlayerBullet*)_object);
break;
}
case GameObject::OT_Enemy://敌机
{
mEnemyPool.append((Enemy*)_object);
break;
}
}
}
void GameObjectPool::Clear()
{
//清除子弹容器
for(auto pBullet : mBulletPool)
{
delete pBullet;
}
//清除敌机容器
for(auto pEnemy :mEnemyPool)
{
delete pEnemy;
}
}
GameObjectPool::~GameObjectPool()
{
Clear();//内存清除
}
plane.h
text
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
#ifndef PLANE_H
#define PLANE_H
#include "gameobject.h"
class Plane : public GameObject
{
public:
explicit Plane(QObject *parent = nullptr);
float MoveSpeed()
{
return mMoveSpeed;
}
protected:
float mMoveSpeed;
int mShootSpeed;
};
#endif // PLANE_H
plane.cpp
text
1
2
3
4
5
6
7
8
#include "plane.h"
Plane::Plane(QObject *parent)
{
mObjectType = GameObject::OT_Player;
}
player.h
text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef MYPLANE_H
#define MYPLANE_H
#include "plane.h"
class Player : public Plane
{
// Q_OBJECT QGraphicsItem的派生类 不支持 Q_OBJECT(老式 信号和槽 宏)
public:
Player();
};
#endif // MYPLANE_H
player.cpp
text
1
2
3
4
5
6
7
8
9
10
11
12
#include "player.h"
Player::Player()
{
this->setPixmap(QPixmap(":/img/Image/MyPlane1.fw.png"));
this->setPos(256,500);
mMoveSpeed = 5;
mShootSpeed = 500;
}
playerbullet.h
text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef PLAYERBULLET_H
#define PLAYERBULLET_H
#include "bullet.h"
class PlayerBullet : public Bullet
{
public:
explicit PlayerBullet(QObject *parent = nullptr);
//玩家子弹音效
void PlaySound();
signals:
public slots:
};
#endif // PLAYERBULLET_H
playerbullet.cpp
text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "playerbullet.h"
PlayerBullet::PlayerBullet(QObject *parent)
{
mObjectType = GameObject::OT_BulletPlayer;
mSpeed = 6;
}
void PlayerBullet::PlaySound()
{
mMedia.setMedia(QUrl("qrc:/sound/music/weapon_player.wav"));
mMedia.play();
}
widget.h
text
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
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QGraphicsPixmapItem>//图形元素
#include <QGraphicsView>//视图
#include <QGraphicsScene>//场景
#include <QList>//链表
#include "enemy.h"
#include "player.h"
#include "bullet.h"
#include <QMediaPlayer>//媒体播放器类
// 图形元素组成 ---> 场景 --> 视图 --> 窗口
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
static Widget* widget;
//按键事件 widget才能检测到这两个函数
void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
text
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
#include "widget.h"
#include "ui_widget.h"
#include "gamecontrol.h"
#include "student.h"
Widget* Widget::widget = nullptr;
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setFixedSize(512,768);
widget = this;//拿到widget,防止使用this报错
GameControl::Instance()->setParent(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::keyPressEvent(QKeyEvent *event)
{
//添加按键到按键组合
switch (event->key())
{
case Qt::Key_W:
case Qt::Key_S:
case Qt::Key_A:
case Qt::Key_D:
GameControl::Instance()->mKeyList.append(event->key());
break;
}
}
void Widget::keyReleaseEvent(QKeyEvent *event)
{
//移除对应按键组合
if(GameControl::Instance()->mKeyList.contains(event->key()))
{
GameControl::Instance()->mKeyList.removeOne(event->key());
}
}
main.cpp
text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <QApplication>
#include "gamecontrol.h"
#include "student.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
//游戏加载
GameControl::Instance()->GameInit();
return a.exec();
}
3.XMl学习
3.1注意点
快速判断xml文件是否写错
将文件直接拖入浏览器中,如果能正常打开就是没错的 HeroInfo.xml
Config.xml
3.2目录结构
目录结构
3.2代码
Hero.h
text
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
#ifndef HERO_H
#define HERO_H
#include <QObject>
class Hero
{
public:
Hero();
Hero(QString name,int atk,int def,int hp,QString des)
{
mName = name;
mAtk =atk;
mHp = hp;
mDef = def;
mDescript = des;
}
QString mName;
int mAtk;
int mDef;
int mHp;
QString mDescript;
};
#endif // HERO_H
Hero.cpp
text
1
2
3
4
5
6
7
8
#include "Hero.h"
Hero::Hero()
{
}
-Widget.h
text
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
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_LoadBtn_clicked();
void on_SaveBtn_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
Widget.cpp
text
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
#include "Widget.h"
#include "ui_widget.h"
#include <QtXML> //需导入QtXML,并且执行qmake
#include "Hero.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
ui->textEdit->setReadOnly(true);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_LoadBtn_clicked()
{
//构建文件对象 QFile
QFile file("HeroInfo.xml");
if(!file.open(QIODevice::ReadOnly))//打开文件用于读取
return;
//**************XML文件读取*************
QDomDocument doc;//创建文档对象
doc.setContent(&file);//设置文档内容,将file和doc绑定起来
// doc.setContent(file.readAll());//设置文档内容
QDomElement root = doc.firstChildElement("Root");//获取第一个子标签
QDomElement hero = root.firstChildElement("Hero");//获取hero子标签
QString text;
while (!hero.isNull())//判断hero标签是否存在
{
text += "Name:" + hero.attribute("name");//读取属性
text += " Atk:" + hero.attribute("atk");
text += " Def:" + hero.attribute("def");
text += " HP:" + hero.attribute("hp");
//读取标签内容
text += " Des:" + hero.text()+"\n";
hero = hero.nextSiblingElement("Hero");//获取下一个Hero兄弟标签
}
ui->textEdit->setText(text);
file.close();
}
void Widget::on_SaveBtn_clicked()
{
QVector<Hero> vec;
vec.push_back(Hero("曹操",100,25,2000,"曹孟德"));
vec.push_back(Hero("曹丕",100,25,2000,"曹子桓"));
vec.push_back(Hero("曹植",100,25,2000,"曹子建"));
vec.push_back(Hero("曹彰",100,25,2000,"曹xx"));
vec.push_back(Hero("曹真",100,25,2000,"曹xx"));
//创建xml文档对象
QDomDocument doc;
QDomElement root = doc.createElement("Root");//创建root标签
for(int i =0;i < vec.count();i++)
{
QDomElement hero = doc.createElement("Hero");//创建hero标签
root.appendChild(hero);
hero.setAttribute("Name",vec[i].mName);//设置属性 Name
hero.setAttribute("Atk",vec[i].mAtk);
hero.setAttribute("Def",vec[i].mDef);
hero.setAttribute("HP",vec[i].mHp);
QDomText text = doc.createTextNode(vec[i].mDescript);
hero.appendChild(text);
}
doc.appendChild(root);//将root节点添加到文档对象
//创建文件对象
QFile file("./Config.xml"); //采用相对路径
if(!file.open(QIODevice::WriteOnly))
return;
QTextStream outStream(&file);//创建文本流对象
doc.save(outStream,4);
file.close();
}