今天分享一个我用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
playplane.cpp:
#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 ) { }
main入口:
#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(); }