首页 > 代码库 > 微信打飞机升级版(Qt实现)

微信打飞机升级版(Qt实现)

今天分享一个我用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();
}