Flash MX 编程深层次应用-Flash射击游戏(3)
作者: 来源: 添加时间:2006-5-23 7:25:516.2.2 我的子弹
1.子弹发射函数
我的飞船要与敌人进行战斗,就得有发射子弹的功能,这里是通过一个函数来实现的。程序代码如下:
function fire (place_x, place_y) {
// 我的飞船发射子弹的最大数是_root.deplicate[1]
my_temp = _root.now_bullet++;
if (my_temp>_root.deplicate[1]) {
my_temp = 0;
}
var new_name = "my_bullet_"+my_temp;
attachMovie("my_plane_bullet", new_name, my_temp+2);
//根据我的飞船的子弹的层数来转向
_root[new_name].gotoAndStop("shot_bullet_"+_root.bullet_level);
_root[new_name]._x = place_x;
_root[new_name]._y = place_y;
_root.now_bullet = my_temp+1;
}
当Flash动画中有大量的可移动物体时,动画的速度将会明显降低。因此,为了加快程序运行速度,就要调整我的飞船发射子弹数的最大值,这个数也就是在屏幕上同时显示的最多子弹数。_root.bullet_level变量存放的是我的子弹的层数,1层就是每次发射1颗子弹,3层就每次发射3颗子弹。这里最大的层数是4层,也就是最多可以发射4颗子弹。有人可能会问,为什么定义这么小的层数,完全可以让它一次发射8颗或者更多子弹。我非常佩服你的勇气,但是可以告诉你的是,如果定义8颗子弹,子弹数增加,程序判断的内容大大增加,这个游戏跑起来就像老牛拉破车一样跑不动了!唉,Flash就是不适合做这种大量物体运动的动画及编程。为了画面的流畅,不得不在游戏情节上做出一些牺牲。
子弹移动函数
上面的代码仅仅是创建子弹的,还需要通过程序让子弹移动:
onClipEvent (enterFrame) {
//子弹每次移动32个点
_parent._y -= 32;
if((_parent._x>_root.play_right)||(_parent._x<_root.play_left)||(_parent._y>_root.play_bottom) || (_parent._y<_root.play_top)) {
_parent.removeMovieClip();
}
}
上面的子弹移动程序非常简单,其实就是完成子弹每一帧自动上移32个点,然后检查子弹是否已经飞出了屏幕;如果飞出了屏幕就把子弹自动清除,这样确保子弹仅出现在可视范围之内。在这里用_parent._y 来控制与检查而不是用_y来控制检查,因为真正动起来的是这个对象的上一层的位置,不是子弹在对象内部的位置。
有人会说,子弹飞出屏幕了还用管,让它随便飞不就成了。如果真的这么想,那么只能说明还没有真正理解编程。一个好的程序员会有一个好的习惯,当用程序创建了一个对象后,如果后面再也不会用到它时就应当把它清除。清除后不但可以保证画面的干净整洁,保持好的整体效果,而且可以回收占用的系统资源,保证后面程序的正常快速运行。对于Flash这种执行效率较低的软件,这一点尤其重要。那么这段程序应该加在什么地方呢?从前面的代码可以看出,程序是自动创建并产生子弹的,那么,这个子弹飞行的程序应该跟子弹画面放在一起,这样才可以保证子弹的正常飞行与判断。在实际制作过程中,将不同级别的子弹放到一个MovieClip中就可以了,如图6-9所示。

图6-9
可以看出,MC(MoviceClip的简称,下同)被分成了5部分,前面4部分分别代表子弹的不同层次和子弹的不同数量。这里就是指向两层子弹的位置,如图6-10所示。

图6-10
它的帧的名字叫:shot_bullet_2,如图6-11所示。

图6-11
4个不同层次的子弹虽然数量不同,但是其制作的方式基本上一致,惟一不同仅仅是变量名而已。如图6-9所示后面的shotted帧是一个击中敌人目标的帧。当击中敌人时,就自动跳到这一帧,显示一段爆炸的动画,然后清除子弹,这样一个击中敌人的过程便告完成。
2.子弹与敌人多对多检测
由于这个MovieClip包括独立的画面和独立的程序,因此把它称做“我的子弹对象”。这样的说法可能不科学,但是却有一定的道理,也便于理解。或者有人会问,我怎么就没有看到检测击中敌人的程序段啊。这个问题提得好,这样就牵出了本游戏最为复杂,也最难解决的一个问题——子弹与敌人如何进行多对多的检测。
先让我们来想想,我的飞船会发射子弹,敌人也会发射子弹。敌人的子弹数目与我的子弹数目都是随机的,随着游戏进程的不断变化,对象名称也在不断变化之中,惟一不变的就只有我的飞船的名称。这样解决了一个问题,可以把敌人的子弹击中“我的飞船”的检测部分的代码封装到敌人的子弹对象里,程序写起来也特别方便。但是,是否也可以把我的子弹击中敌人的代码封装到我的子弹对象里呢?经过思考后觉得这样太麻烦。因为敌人是会经常变化的,可能这阵子是这个敌人,过一阵这个敌人又走了,变成了另外一个敌人。如果把检测部分封装在子弹对象里,就得有一个数组来专门存放敌人的名单。在检测时让子弹跟这个敌人名单一一检查,查看它是否击中敌人。如果击中,就让它爆炸,并且从数组中删除它。但了解Flash的人知道,从数组中删除一个值的程序执行效率是非常低的,于是笔者对这种解决办法持怀疑态度。
另一种解决办法,就是把检测程序放到敌人中,也就是让敌人来检测子弹是否击中它!这个好像容易一些,因为子弹数目最多有_root.deplicate[1]个,一个循环语句就可以搞定,程序简单很多。但是同时又带来另一个问题,这个_root.deplicate[1]的值将明显影响动画的速度。如果它很大,游戏的空循环次数将大大增加,因为一颗子弹不可能同时击中几个敌人。这样就明显增加了空循环的次数,会影响游戏的响应速度,严重时甚至会影响游戏的流畅程度。
经过上面两种办法的比较,最终选择了简单的办法,让敌人检测子弹!这种做法虽然不一定是最好的办法,却是最容易理解的办法,也容易按面向对象的方法来进行编程。经过这么处理,_root.deplicate[1]变量就变得更加重要起来,它将承担起整个游戏速度上的重任!在最后阶段的运行测试中,这个值的确对游戏的速度造成了很大的影响。因此,通过对这个值进行大量的测试,不断地调整其大小,根据游戏速度上的变化,最终决定了它的值为18。这个18代表18排子弹。也就是说,如果一排有4颗子弹的话,那同时就有18×4=72(颗)子弹了!
6.2.3 游戏中的令牌
从上一节的内容可以看出,我的子弹威力是可以调节的。这是通过变量_root.bullet_level来实现的。玩过战斗游戏的人就可以知道,有些敌人在被打死之后可以放出一些令牌。这些令牌有的可以增加我的武器的威力;有的可以增加我的血量;有的可以增加命的条数;有的甚至可以让自己的飞船加上金刚不坏身躯。本游戏一共设置了3种令牌,这3种令牌的图形如图6-12所示,从左到右分别是加生命、加血与加武器。

图6-12
首先看看放这3种令牌的程序,请看下面代码:
function put_plane (place_x, place_y) {
//放增加飞船的命的程序
var new_name = "plane_"+(++_root.last_pai);
attachMovie("add_plane", new_name, _root.last_pai+_root.deplicate[6]+3);
_root[new_name]._x = place_x;
_root[new_name]._y = place_y;
}
function put_blood (place_x, place_y) {
//放加血量令牌程序
var new_name = "blood_"+(++_root.last_pai);
attachMovie("blood", new_name, _root.last_pai+_root.deplicate[6]+1);
_root[new_name]._x = place_x;
_root[new_name]._y = place_y;
}
function put_pai (place_x, place_y) {
// 放增加武器威力令牌程序
var new_name = "pai_"+(++_root.last_pai);
attachMovie("pai", new_name, _root.last_pai+_root.deplicate[6]);
_root[new_name]._x = place_x;
_root[new_name]._y = place_y;
}
这3个程序的功能其实就是创建并产生令牌,并将令牌放在相应的位置。从程序代码看,这些程序并没有让令牌移动,其实令牌的移动已经被放到令牌对象中了。
1.加命控制
为了讲解加命程序,我得先讲讲我的可控飞船数的制作。先把我的飞船缩到很小,然后做5个这样的东西,图中的红色色条就是前面说到的我的血量控制条,如图6-13所示。

图6-13
它们从左到右的名字分别是:my_plane_num_1,my_plane_num_2,…,my_plane_num_5,如图6-14所示。

图6-14