承接上篇,咱来继续说:
四、爆炸动画的实现
当战机和敌机发生碰撞的时候,本程序设定会显示一个爆炸的动画。对于爆炸的显示,原本的想法是利用QMovie显示爆炸的gif动态图,但是这样的话会阻碍绘图事件和定时器事件,我尝试了一下,程序每次都会崩(当然,如果重新建立一个线程。可能会行)。后来转变了思路,爆炸动画的显示和飞机移动动画的显示基本上不是一个东西吗?不是可以利用重绘事件和定时器事件 来显示动画吗?照着这个思路。主要代码如下:
//重绘事件中绘制爆炸
foreach(Explosion * temp_exp,exp_list)
{
if(temp_exp->GetCurPicNum()<=EXPLOSION_IMG_NUM)
painter.drawPixmap(temp_exp->GetPos(),*(temp_exp->exp_img_list[temp_exp->GetCurPicNum()-1]));
}
if(IsAttacked == true ) //子弹攻击到敌机
{
EneFishIsAttacked();
Explosion *temp_exp = new Explosion; //产生爆炸
temp_exp->SetPos(temp_fish->pos);
exp_list.append(temp_exp);
myPlane->my_bulles.removeOne(*iter_bul); //移除子弹
delete temp_bul;
fish_list.removeOne(*iter); //小虾米碰撞到子弹,删除敌机
delete temp_fish;
break;
}
//判断我方战机是否碰撞到敌方战机
QRect myPlane_rect(myPlane->pos,QSize(myPlane->img.width(),myPlane->img.height()));
IsCon = temp.intersects(myPlane_rect);
if(IsCon == true)
{
Explosion *temp_exp = new Explosion; //产生爆炸
temp_exp->SetPos(temp_fish->pos);
exp_list.append(temp_exp);
fish_list.removeOne(*iter); //小虾米碰撞到我方飞机,删除敌机
delete temp_fish;
EneFishIsAttacked();
MyPlaneIsAttacked(); //我方战机损失生命力
break;
}
//切换爆炸图片,显示爆炸
void Widget::ShowExplosion()
{
QLinkedList::iterator iter;
for(iter = exp_list.begin();iter !=exp_list.end();++iter) //遍历爆炸列表,更换下一张爆炸图片
{
Explosion *temp_exp = *iter;
if(temp_exp->NextPic() == false) //爆炸显示结束,删除爆炸
{
exp_list.removeOne(*iter);
delete temp_exp;
}
}
}
上述代码的主要含义就是利用定时器事件,在检测战机和敌机、敌机和子弹发生碰撞了之后,就会产生一个爆炸,然后在重绘事件中绘制这个爆炸。当然,还得再定时器事件中切换爆炸的图片。产生的效果如下:
分离的爆炸图片是8个bmp图像,csdn上不能上传,我把它放在我的资源里了,有需要的可以下载使用。
五、飞机图片透明效果的实现
在图像的绘制中,飞机是一张背景为黑色的图片。如下图所示,
需要把飞机的边缘遮掩住。这里使用了QPixmap自带的一个遮掩函数QPixmap::setmask(mask)函数。代码如下:
//使飞机边框透明
img.load(":/img/myplane.bmp");
QBitmap mask = img.createMaskFromColor(QColor(0,0,0),Qt::MaskInColor);
img.setMask(mask);
照我的理解:这个函数可以根据你的mask把显示的图片中相应的部分变成透明的。在这里我设置黑色为透明的。产生的效果如下:
同理,敌机图片的边缘和爆炸图片的边缘都是这样产生透明效果的。
六、背景图的移动
为了使飞机产生不断前进的效果,需要把背景不断移动。根据上面的思路,对,这里也是产生相同的做法。利用定时器和重绘事件,不断重绘背景图片。具体来说,采用 两张图片,一张图片不断往下移动,另一张不断从上补充。(这里的两张背景图片采用的是相同的图片,所以中间衔接的地方有些不流畅)。主要代码如下:
//绘制动态背景
painter.drawPixmap(0,back_spe,back);
painter.drawPixmap(0,back_spe-back.height(),back);
这里painter.draw()函数可以指定绘制图像的区域。
七、游戏状态的转换
本程序采用了三个状态,开始欢迎状态,运行状态和结束状态。这三种状态都是在一个QWidget中切换的。开始状态时,点击回车键,进入运行状态;运行状态,玩家over时,进入结束状态;结束状态,点击回车键,进入运行状态。代码嵌入在其他的步骤中了,在此就不贴了。三种状态的图片如下:
这里采用的方法也有点low,就是用if语句判断当前的状态,然后进行响应的操作。这在重绘事件和按键事件中都需要进行判断。(比较高级点的方法应该是利用状态机来实现,现在弄得还不是很清楚,暂且就不用了吧)
八、不足与展望
稍微正式点,最后说点不足和展望。作为一个简易版的飞机大战,本程序不足之处有以下
1、未添加音效,如爆炸音效。
2、未添加闯关模式、大招模式
3、未添加敌机的Boss
4、未采用好的框架。(如图形视图框架、动画框架和状态机框架)
。。。。
这是1.0版,等什么时候学的再深点,在编出2.0版的。
最后的整个工程的源码见:点这儿