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();


}














原文地址:https://blog.csdn.net/qq_48621751/article/details/123227303?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168949707716800215098447%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168949707716800215098447&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-26-123227303-null-null.142^v88^insert_down1,239^v2^insert_chatgpt&utm_term=%E6%80%8E%E4%B9%88%E6%89%93%E5%BC%80qt6%E9%A1%B9%E7%9B%AE