今天分享一个我用Qt改写的微信打飞机游戏,程序重在设计过程,运用了(抽象)工厂的模式产生不同的对象(子弹,敌军,炸弹...),逻辑处理相对简单,尚未解决的问题是怎么使用Qt实现各种声音的混音效果,这里使用QThread线程里面使用Sound类播放声音,但是效果不佳,如有好的解决方案,望回复指导
,程序运行效果如下:
所有类的定义和游戏实现过程都在头文件playplane.h和源文件playplane.cpp中
playplane.h:
#ifndef PLAYPLANE_H
#define PLAYPLANE_H
#include <QtGui/QWidget>
#include <QFile>
#include <QDomDocument>
#include <QMessageBox>
#include <QDir>
#include <QSound>
#include <QTimer>
#include <QCursor>
#include <QMouseEvent>
#include <QPainter>
#include <QThread>
#include <QTextCodec>
class Entity
{
public:
QString Name;
int X;
int Y;
int Width;
int Height;
int SpeedY;
int SpeedX;
QImage Image;
QString ToString()
{
return Name;
}
};
//飞机类
class Plane :public Entity
{
public:
int Level;
Plane(){
this->Name = "";
this->X = 0;
this->Y = 0;
this->Width = 0;
this->Height = 0;
this->SpeedX = 0;
this->SpeedY = 0;
this->Image = QImage();
this->Level = 1;
}
Plane(QString name, int x, int y, int width, int height, int speedX,int speedY, QImage bmp)
{
this->Name = name;
this->X = x;
this->Y = y;
this->Width = width;
this->Height = height;
this->SpeedX = speedX;
this->SpeedY = speedY;
this->Image = bmp;
this->Level = 1;
}
void LevelUp()
{
Level++;
}
};
//子弹类
class Bullet:public Entity
{
public:
Bullet(QString name, int x, int y, int width, int height, int speedX, int speedY, QImage bmp)
{
this->Name = name;
this->X = x;
this->Y = y;
this->Width = width;
this->Height = height;
this->SpeedX = speedX;
this->SpeedY = speedY;
this->Image = bmp;
}
};
//敌军类
class Enemy:public Entity
{
public:
int HP;
Enemy(QString name, int x, int y, int width, int height, int speedX, int speedY, int hp, QImage bmp)
{
this->Name = name;
this->X = x;
this->Y = y;
this->Width = width;
this->Height = height;
this->SpeedX = speedX;
this->SpeedY = speedY;
this->HP = hp;
this->Image = bmp;
}
};
//奖励类
class Reward:public Entity
{
public:
int StnTimes;
int Counter;
Reward(QString name, int x, int y, int width, int height, int speedX, int speedY, int stnTimes, QImage bmp)
{
this->Name = name;
this->X = x;
this->Y = y;
this->Width = width;
this->Height = height;
this->SpeedX = speedX;
this->SpeedY = speedY;
this->StnTimes = stnTimes;
this->Image = bmp;
this->Counter = 0;
}
};
//爆炸效果类
class Explosion
{
public:
int X;
int Y;
int Width;
int Height;
int StnTimes;
int Counter;
QList<QImage> Images;
Explosion(int x, int y, int stnTimes,QList<QImage> bmp)
{
this->X = x;
this->Y = y;
this->StnTimes = stnTimes;
this->Images = bmp;
this->Counter = 0;
}
};
//实体构建的工厂类
class EntityFactory
{
public:
enum ImgItem { boom_add = 1, bomb_icon = 2, bullet_0 = 3, bullet_1 = 4, bullet_add = 5,
enemy_b = 6, enemy_m = 7, enemy_s = 8, explosion_01 = 9, explosion_02 = 10,
explosion_03 = 11, hero_1 = 12, hero_2 = 13, pause_button = 14, resume_button = 15,
smoke_01 = 16, smoke_02 = 17 };
static QImage image_item[18];
static void InitFactory(QString xmlPath);
static Plane GenPlane(QString style);
static Enemy GenEnemy(QString size,int speedBase,int width);
static Bullet GenBullet(QString style,int p_x,int p_y);
static Reward GenReward(QString style, int p_x, int p_y);
static QImage GetBoomIcon();
static Explosion GenExplosion(QString style, int p_x, int p_y);
};
class MusicPlay
{
private:
QString musicPath;
QSound* bells;
public:
MusicPlay(QString musicPath)
{
this->musicPath = musicPath;
bells = new QSound(musicPath);
}
~MusicPlay()
{
delete bells;
}
void Play()
{
bells->play();
return;
}
};
class ThreadMusicPlay:public QThread
{
private:
QString musicPath;
int msec;
public:
ThreadMusicPlay(QString musicPath = "",int msec = 100);
~ThreadMusicPlay();
void setFilePath(QString musicPath,int msec);
void run();
};
class PlayPlane : public QWidget
{
Q_OBJECT
public:
PlayPlane(QWidget *parent = 0, Qt::WFlags flags = 0);
~PlayPlane();
public slots:
void timedraw();
protected:
void paintEvent ( QPaintEvent * );
void mouseMoveEvent ( QMouseEvent * );
void mousePressEvent ( QMouseEvent * );
void keyPressEvent ( QKeyEvent * );
void leaveEvent ( QEvent * );
void enterEvent ( QEvent * );
private:
Plane plane;
QTimer t_draw;
QList<Enemy> enemy_lsit;
QList<Bullet> bullet_lsit;
QList<Explosion> explosion_list;
QList<Reward> reward_list;
int score;
int boom_count;
bool pause;
QImage background;
int block_time;
int block_interval;
int send_time;
int send_interval;
int reward_time;
int reward_interval;
int rwd_bullet_stnTime;
int backY;
bool b_GameOver;
MusicPlay* music_shoot;
MusicPlay* music_BOMB3;
MusicPlay* music_explosion;
ThreadMusicPlay threadMusicPlay;
};
#endif // PLAYPLANE_H
#include "playplane.h"
QImage EntityFactory::image_item[18];
void EntityFactory::InitFactory(QString xmlPath)
{
QFile file(xmlPath);
if (!file.open(QIODevice::ReadOnly | QFile::Text)) {
QMessageBox::warning(NULL,"error","open for read error...");
}
QString errorStr;
int errorLine;
int errorColumn;
QDomDocument doc;
if (!doc.setContent(&file, false, &errorStr, &errorLine, &errorColumn)) {
QMessageBox::warning(NULL,"error","setcontent error...");
file.close();
}
file.close();
QDir filedir(xmlPath);
QDomElement root = doc.documentElement();
if (root.tagName() != "TextureAtlas") {
QMessageBox::warning(NULL,"error","root.tagname != TextureAtlas...");
}
filedir.cdUp();
QString imagepath = filedir.path() + "/" + root.attribute("imagePath");
QImage bmp = QImage(imagepath);
QDomNode node = root.firstChild();
int i = 1;
while(!node.isNull())
{
if(node.isElement())
{
QDomElement element = node.toElement();
QString name = element.attribute("name");
int x = element.attribute("x").toInt();
int y = element.attribute("y").toInt();
int width = element.attribute("width").toInt();
int height = element.attribute("height").toInt();
image_item[i++] = bmp.copy(x, y, width, height);
}
node = node.nextSibling();
}
}
Plane EntityFactory::GenPlane(QString style)
{
if (style == "normal")
{
QImage tempBmp = image_item[(int)ImgItem::hero_1];
return Plane("small", 250,500, tempBmp.width(), tempBmp.height(),0, 0, tempBmp);
}
else if (style == "super")
{
QImage tempBmp = image_item[(int)ImgItem::hero_2];
return Plane("mid", 350, 700, tempBmp.width(), tempBmp.height(), 0,0, tempBmp);
}
}
Enemy EntityFactory::GenEnemy(QString size,int speedBase,int width)
{
if (size == "small")
{
QImage tempBmp = image_item[(int)ImgItem::enemy_s];
return Enemy("small", qrand()%width+50, 0, tempBmp.width(), tempBmp.height(),qrand()%5-2, qrand()%4+2+speedBase,1,tempBmp);
}
else if (size == "mid")
{
QImage tempBmp = image_item[(int)ImgItem::enemy_m];
return Enemy("mid", qrand()%width + 50, 0, tempBmp.width(), tempBmp.height(), qrand()% 5 - 2, qrand()% 4 + 1+speedBase, 5, tempBmp);
}
else if (size == "big")
{
QImage tempBmp = image_item[(int)ImgItem::enemy_b];
return Enemy("big", qrand()%width + 50, 0, tempBmp.width(), tempBmp.height(), qrand()% 3 - 1, qrand()% 3 + 1+speedBase, 20, tempBmp);
}
}
Bullet EntityFactory::GenBullet(QString style,int p_x,int p_y)
{
if (style == "red")
{
QImage tempBmp = image_item[(int)ImgItem::bullet_0];
return Bullet("small", p_x, p_y, tempBmp.width(), tempBmp.height(),0, 20, tempBmp);
}
else if (style == "blue")
{
QImage tempBmp = image_item[(int)ImgItem::bullet_1];
return Bullet("mid", p_x, p_y, tempBmp.width(), tempBmp.height(),0, 20, tempBmp);
}
}
Reward EntityFactory::GenReward(QString style, int p_x, int p_y)
{
if (style == "bullet_add")
{
QImage tempBmp = image_item[(int)ImgItem::bullet_add];
return Reward("bullet_add", p_x, p_y, tempBmp.width(), tempBmp.height(), qrand() % 5 - 2, 3,5000, tempBmp);
}
else if (style == "boom_add")
{
QImage tempBmp = image_item[(int)ImgItem::boom_add];
return Reward("boom_add", p_x, p_y, tempBmp.width(), tempBmp.height(), qrand() % 5 - 2, 3,5000, tempBmp);
}
}
QImage EntityFactory::GetBoomIcon()
{
return image_item[(int)ImgItem::bomb_icon];
}
Explosion EntityFactory::GenExplosion(QString style, int p_x, int p_y)
{
if (style == "small")
{
QList<QImage> tempBmp;
tempBmp.append(image_item[(int)ImgItem::explosion_01]);
tempBmp.append(image_item[(int)ImgItem::explosion_02]);
tempBmp.append(image_item[(int)ImgItem::explosion_03]);
tempBmp.append(image_item[(int)ImgItem::explosion_02]);
tempBmp.append(image_item[(int)ImgItem::explosion_01]);
return Explosion(p_x, p_y, 300, tempBmp);
}
else if (style == "mid")
{
QList<QImage> tempBmp;
tempBmp.append(image_item[(int)ImgItem::explosion_01]);
return Explosion(p_x, p_y, 500, tempBmp);
}
else if (style == "big")
{
QList<QImage> tempBmp;
tempBmp.append(image_item[(int)ImgItem::explosion_01]);
return Explosion(p_x, p_y, 500, tempBmp);
}
}
ThreadMusicPlay::ThreadMusicPlay(QString musicPath,int msec)
{
this->musicPath = musicPath;
this->msec = msec;
}
ThreadMusicPlay::~ThreadMusicPlay()
{
}
void ThreadMusicPlay::setFilePath(QString musicPath,int msec)
{
this->musicPath = musicPath;
this->msec = msec;
}
void ThreadMusicPlay::run()
{
QSound::play(this->musicPath);
this->msleep(msec);
}
PlayPlane::PlayPlane(QWidget *parent, Qt::WFlags flags)
: QWidget(parent, flags)
{
score = 0;
boom_count = 5;
pause = false;
block_time = 1;
block_interval = 0;
send_time = 0;
send_interval = 0;
reward_time = 1;
reward_interval = 0;
rwd_bullet_stnTime = 0;
backY = 0;
EntityFactory::InitFactory("resource/plane.xml");
background = QImage("resource/bg_02.jpg");
plane = EntityFactory::GenPlane("normal");
this->setCursor(Qt::BlankCursor);
this->cursor().setPos(QPoint(plane.X + this->x(), plane.Y + this->y()));
connect(&t_draw,SIGNAL(timeout()),this,SLOT(timedraw()));
t_draw.setInterval(20);
send_interval = 100 / t_draw.interval();
block_interval = 260 / t_draw.interval();
reward_interval = 5000 / t_draw.interval();
t_draw.start();
music_BOMB3 = new MusicPlay("resource/BOMB3.wav");
music_shoot = new MusicPlay("resource/shoot.wav");
music_explosion = new MusicPlay("resource/explosion.wav");
b_GameOver = false;
setMouseTracking(true);
}
PlayPlane::~PlayPlane()
{
delete music_BOMB3;
delete music_shoot;
delete music_explosion;
}
void PlayPlane::timedraw()
{
t_draw.stop();
if(pause)
{
update();
t_draw.start();
return;
}
//发射子弹
if (send_time > send_interval)
{
if (rwd_bullet_stnTime > 0)//双弹未结束
{
bullet_lsit.push_back(EntityFactory::GenBullet("blue", plane.X - 6, plane.Y - 50));
bullet_lsit.push_back(EntityFactory::GenBullet("blue", plane.X + 6, plane.Y - 50));
rwd_bullet_stnTime -= t_draw.interval() * send_interval;
}
else
{
bullet_lsit.push_back(EntityFactory::GenBullet("red", plane.X, plane.Y - 50));
}
threadMusicPlay.setFilePath("resource/shoot.wav",100);
threadMusicPlay.start();
send_time = 0;
}
//生产敌军
if (block_time % block_interval == 0)
{
int speedBase = 0;
if (block_interval < 2)
speedBase = 1;
if (block_interval < 5)
speedBase = 2;
else if (block_interval < 10)
speedBase = 1;
if (block_time % (block_interval * 20) == 0)
{
enemy_lsit.push_back(EntityFactory::GenEnemy("big",speedBase,this->width()));
}
else if(block_time % (block_interval * 10) == 0)
{
enemy_lsit.push_back(EntityFactory::GenEnemy("mid", speedBase,this->width()));
}
else
{
enemy_lsit.push_back(EntityFactory::GenEnemy("small", speedBase,this->width()));
}
}
//奖励
if (reward_time == reward_interval)
{
if (qrand() % 2 == 0)
{
reward_list.push_back(EntityFactory::GenReward("bullet_add", qrand() %(this->width())+50, 0));
}
else
{
reward_list.push_back(EntityFactory::GenReward("boom_add", qrand() %(this->width())+50, 0));
}
reward_time = 0;
}
send_time++;
block_time++;
reward_time++;
//飞机提升水平
if (send_interval>0 && score > plane.Level * plane.Level * 50000)
{
plane.LevelUp();
send_interval--;
}
//敌军提升水平
if (block_interval > 1 && block_time % 300 == 300-1)
{
block_interval--;
}
//碰撞
for (int i = 0; i < enemy_lsit.size(); i++)
{
for (int j = 0; j < bullet_lsit.size(); j++)
{
if (qAbs(bullet_lsit[j].X - enemy_lsit[i].X) < (bullet_lsit[j].Width + enemy_lsit[i].Width) / 2 && qAbs(bullet_lsit[j].Y - enemy_lsit[i].Y) < (bullet_lsit[j].Height + enemy_lsit[i].Height) / 2)
{
enemy_lsit[i].HP--;
if (enemy_lsit[i].HP == 0)//explose
{
//socre ++
if (enemy_lsit[i].Name == "small") score += 1000;
else if (enemy_lsit[i].Name == "mid") score += 6000;
else if (enemy_lsit[i].Name == "big") score += 25000;
//add to explosion
explosion_list.push_back(EntityFactory::GenExplosion("small", enemy_lsit[i].X, enemy_lsit[i].Y));
threadMusicPlay.setFilePath("resource/explosion.wav",200);
threadMusicPlay.start();
//remove both
enemy_lsit.removeAt(i);
bullet_lsit.removeAt(j);
}
else
{
bullet_lsit.removeAt(j);
}
break;
}
}
}
//获取奖励
for (int i = 0; i < reward_list.size(); i++)
{
if (qAbs(plane.X - reward_list[i].X) < (plane.Width + reward_list[i].Width) / 2 && qAbs(plane.Y -reward_list[i].Y) < (plane.Height + reward_list[i].Height) / 2)
{
if (reward_list[i].Name == "bullet_add")
{
rwd_bullet_stnTime += reward_list[i].StnTimes;
}
else if (reward_list[i].Name == "boom_add")
{
boom_count++;
}
reward_list.removeAt(i);
}
}
//飞机碰撞检测
for (int i = 0; i < enemy_lsit.size(); i++)
{
bool isCrashed = false;
if (qAbs(plane.X - enemy_lsit[i].X) < (plane.Width / 4 + enemy_lsit[i].Width) / 2 && qAbs(plane.Y -enemy_lsit[i].Y) < (plane.Height - 30 + enemy_lsit[i].Height) / 2)
{
isCrashed = true;
}
if (isCrashed)
{
t_draw.stop();
this->setCursor(Qt::ForbiddenCursor);
threadMusicPlay.setFilePath("resource/BOMB5.wav",200);
threadMusicPlay.start();
b_GameOver = true;
update();
return;
}
}
update();
t_draw.start();
}
void PlayPlane::paintEvent ( QPaintEvent * e )
{
QPainter painter(this);
//背景图
painter.drawImage(QRect(0, - this->height() + backY, this->width(),this->height()),background);
painter.drawImage(QRect(0, backY, this->width(),this->height()),background);
backY += 2;
if (backY > this->height())
backY = 0;
//飞机
painter.drawImage(QPoint(plane.X - plane.Width / 2, plane.Y - plane.Height / 2),plane.Image);
//子弹
for (int i = 0; i < bullet_lsit.size(); i++)
{
painter.drawImage(QPoint(bullet_lsit[i].X - bullet_lsit[i].Width / 2, bullet_lsit[i].Y -bullet_lsit[i].Height / 2),bullet_lsit[i].Image);
bullet_lsit[i].Y -= bullet_lsit[i].SpeedY;
if (bullet_lsit[i].Y < -40)
{
bullet_lsit.removeAt(i);
}
}
//奖励
for (int i = 0; i < reward_list.size(); i++)
{
painter.drawImage(QPoint(reward_list[i].X - reward_list[i].Width / 2, reward_list[i].Y -reward_list[i].Height / 2),reward_list[i].Image);
reward_list[i].Y += reward_list[i].SpeedY;
reward_list[i].X += reward_list[i].SpeedX;
if (reward_list[i].Y > this->height() + 20)
{
reward_list.removeAt(i);
}
}
//炸弹
QImage boom_icon = EntityFactory::GetBoomIcon();
if (boom_count > 0)
{
painter.drawImage(QPoint(10, this->height() - 40 - boom_icon.height()),boom_icon);
QFont font("微软雅黑",18);
painter.setFont(font);
painter.setPen(Qt::gray);
painter.drawText(QPoint(10 + boom_icon.width(),this->height() -15 - boom_icon.height()),"×" + QString::number(boom_count));
}
//敌军
for (int i = 0; i < enemy_lsit.size(); i++)
{
painter.drawImage(QPoint(enemy_lsit[i].X - enemy_lsit[i].Width / 2, enemy_lsit[i].Y -enemy_lsit[i].Height / 2),enemy_lsit[i].Image);
enemy_lsit[i].Y += enemy_lsit[i].SpeedY;
enemy_lsit[i].X += enemy_lsit[i].SpeedX;
if (enemy_lsit[i].X > this->width() || enemy_lsit[i].X < 0)
{
enemy_lsit[i].SpeedX = -enemy_lsit[i].SpeedX;
}
if (enemy_lsit[i].Y > this->width() + 20)
{
enemy_lsit.removeAt(i);
}
}
//爆炸效果
for (int i = 0; i < explosion_list.size(); i++)
{
QImage temp_explose = explosion_list[i].Images[explosion_list[i].Counter / (explosion_list[i].StnTimes /explosion_list[i].Images.size())];
painter.drawImage(QPoint(explosion_list[i].X - temp_explose.width() / 2, explosion_list[i].Y -temp_explose.height() / 2),temp_explose);
explosion_list[i].Counter += 24;
if (explosion_list[i].Counter > explosion_list[i].StnTimes)
explosion_list.removeAt(i);
}
QFont font("微软雅黑",14);
painter.setFont(font);
painter.setPen(Qt::green);
painter.drawText(QPointF(10, 20),"分数:" + QString::number(score));
painter.drawText(QPointF(this->width() - 90, 20),"等级:" + (send_interval == 1 ? "满级" : QString::number(plane.Level)));
if(b_GameOver)
{
QFont font("微软雅黑",22);
painter.setFont(font);
painter.setPen(Qt::red);
painter.drawText(QPointF(this->width()/2-80, this->height()/2-50),QString("Game Over"));
return;
}
if(pause)
{
QFont font("微软雅黑",22);
painter.setFont(font);
painter.setPen(Qt::red);
painter.drawText(QPointF(this->width()/2-30, this->height()/2-50),QString("暂 停"));
return;
}
}
void PlayPlane::mouseMoveEvent ( QMouseEvent * e )
{
if(b_GameOver)
{
return;
}
if (!pause)
{
plane.X = e->x();
plane.Y = e->y();
}
}
void PlayPlane::mousePressEvent ( QMouseEvent * e )
{
if (!pause && e->button() == Qt::RightButton)
{
if (boom_count > 0)
{
boom_count--;
for (int i = 0; i < enemy_lsit.size(); i++)
{
//socre ++
if (enemy_lsit[i].Name == "small") score += 1000;
else if (enemy_lsit[i].Name == "mid") score += 6000;
else if (enemy_lsit[i].Name == "big") score += 25000;
//add to explosion
explosion_list.push_back(EntityFactory::GenExplosion("small", enemy_lsit[i].X, enemy_lsit[i].Y));
}
threadMusicPlay.setFilePath("resource/BOMB3.wav",500);
threadMusicPlay.start();
enemy_lsit.clear();
}
}
}
void PlayPlane::keyPressEvent ( QKeyEvent * e )
{
if(b_GameOver)
{
return;
}
if (e->key() == ‘ ‘)
{
pause = !pause;
if(pause)
{
t_draw.stop();
this->setCursor(Qt::ForbiddenCursor);
update();
}
else
{
t_draw.start();
this->setCursor(Qt::BlankCursor);
this->cursor().setPos(QPoint(plane.X + this->x(), plane.Y + this->y()));
}
}
}
void PlayPlane::leaveEvent ( QEvent * e )
{
}
void PlayPlane::enterEvent ( QEvent * e )
{
}
#include "playplane.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB2312"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312"));
PlayPlane w;
w.resize(480,600);
w.show();
return a.exec();
}
原文:http://blog.csdn.net/u012739657/article/details/25183493