Flash MX 编程深层次应用-Flash射击游戏(4)
作者: 来源: 添加时间:2006-5-23 7:25:56在初始化时需用程序把多余的部分隐藏起来,仅显示正常飞船的数。其程序如下:
for (i=5; i>=my_plane_number; i--) {
eval("my_plane_num_"+i)._visible = false;
}
当我的飞船爆炸时就同时减少一个显示,代码如下:
_root["my_plane_num_"+(_root.my_plane_number-1)]._visible = false;
_root.my_plane_number--;
这个代码已经包含在前面的飞船控制程序中了。如果此时我们接到一个令牌,就应先对_root.my_plane_number加1,再将对应的小飞船显示出来。
最后,来看看加命令牌中的程序:
onClipEvent (enterFrame) {
_parent._x += random(6)-3;
_parent._y += 5;
if((_parent._x>_root.play_width)||(_parent._x<0) || (_parent._y>_root.play_height) || (_parent._y<0)) {
_parent.removeMovieClip();
}
//如果我的飞船与令牌相撞击,就表示我已经接到令牌
if (_parent.hitTest(_root.my_plane)) {
// 播放接到令牌时的声音
_root.get_pai.start();
_root["my_plane_num_"+_root.my_plane_number]._visible = true;
_root.my_plane_number++;
//如果接到令牌后,就清除之
_parent.unloadMovie();
}
}
在这里,令牌移动的x坐标位置在正负3个点之内,而y坐标位置每次向下移动5个点,这样就保证了令牌的随机下落,而不会呆板地直线下落。当然了,令牌如果移出屏幕还没有被飞船接到,就清除它。
2.增加血量
同理,增加血量令牌程序如下:
onClipEvent (enterFrame) {
_parent._x += random(6)-3;
_parent._y += 5;
if((_parent._x>_root.play_width)||(_parent._x<0) || (_parent._y>_root.play_height) || (_parent._y<0)) {
_parent.removeMovieClip();
}
if (_parent.hitTest(_root.my_plane)) {
// 播放接到令牌时的声音
_root.get_pai.start();
if (_root.my_blood<50) {
//一次加血15点,当然了血不能超过最大值50
_root.my_blood += 15;
_parent.unloadMovie();
if (_root.my_blood >50) {
_root.my_blood = 50;
}
} else {
//如果血已经加满就加分200
_root.your_score += 200;
_parent.gotoAndPlay("add_fen");
}
}
}
3.增强武器威力
增加武器威力令牌程序如下:
onClipEvent (enterFrame) {
_parent._x += random(6)-3;
_parent._y += 5;
if((_parent._x>_root.play_width)||(_parent._x<0) || (_parent._y>_root.play_height) || (_parent._y<0)) {
_parent.removeMovieClip();
}
if (_parent.hitTest(_root.my_plane)) {
//播放声音
_root.get_pai.start();
//武器威力不能超过最大值
if (_root.bullet_level < _root.powder.length) {
_root.bullet_level ++;
_parent.removeMovieClip();
} else {
//如果武器威力已经到最大值则加200分
_root.your_score +=200;
_parent.gotoAndPlay("add_fen");
}
}
}
6.2.4 游戏中的普通敌人
前面讲述了如何控制我的飞船,下面将讲讲游戏中的敌人。在游戏中敌人的内容占据了相当大的比重。为了增加游戏的可玩性与趣味性,敌人又分成很多类,有发射直线子弹的;有发射跟踪子弹的;有一次发射3颗子弹的,还有发射爆炸弹的;有些敌人移动快,有些移动慢;有些敌人只会左右移动,有些敌人只会从上往下移动,而有些敌人可以按一定的路线移动,所有这些特性全都要通过动画或者编程来完成。由于普通敌人相当多,而且复杂,不可能讲清楚所有的敌人,因此选择了几个有代表性的敌人来讲解。
1.直线运动的敌人
程序如下:
onClipEvent (load) {
//定义一下敌人血液值
enemy_blood = 6;
half_height = _height>>1;
}
onClipEvent (enterFrame) {
//这后面的两句是敌人移动的核心语句
_parent._parent.enemy1_main_x = _parent._x+_parent._parent._x;
_parent._parent.enemy1_main_y = _parent._y+_parent._parent._y+half_height;
for (i=0; i<=_root.max_bullet_num; i++) {
//按前面讲到的方法,检查敌人是否撞到我的排子弹上
if (!_root["my_bullet_"+i].shotted_check) {
if (_parent.hitTest(_root["my_bullet_"+i])) {
//如果子弹射中了敌人,那么让子弹变成子弹击中目标效果
_root["my_bullet_"+i].gotoAndPlay("shotted");
_root["my_bullet_"+i].shotted_check = true;
//我的子弹击中敌人,我的得分增加,敌人的血量减少
_root.your_score += 20;
enemy_blood -=_root.bullet_level;
}
}
}
//如果敌人撞到了我的飞船,那敌人的血量与我的飞船的血量都要减少
if (_parent.hitTest(_root.my_plane)) {
enemy_blood -=_root.bullet_level;
_root.my_blood -= 4;
}
//如果敌人的血没有了,那敌人就死亡了
if (enemy_blood<=0) {
//敌人死了,就让它停止运动,然后爆炸,给我加分
_parent._parent.stop();
_root.your_score += 20;
_parent.gotoAndPlay("bomb");
}
}
这段程序中最核心的那两句是求出敌人的当前位置。它主要的目的是求出敌人发射子弹时子弹的初始位置。由于在考虑的时候采用的是面向对象的方法,也就是说把程序与画面封装起来,所以采用的是相对路径的方式。这段程序被加载在敌人的MovieClip上,然后再让这个MovieClip按直线移动。这里直线移动既可以上下移动,还可以左右移动,甚至斜向移动,这样就保证了敌人移动方式与程序控制的各自独立性。虽然程序代码有点难懂,但是在调试成功之后其各自独立性给动画制作方面省了不少功夫,因为此时的动画制作已经完全不用考虑程序上的配合了!如图6-15所示。

图6-15
通过这种方式,我制作了快速下飞的敌人、慢速下飞的敌人、从右往左飞的以及从左往右飞的敌人。
在游戏的Library面板中包括了多个MovieClip,按一定的规则给它们取名字,这样可以轻松地看出它们的层次调用关系:
原始飞机图片→敌人程序→敌人移动动画
通过这样一个MC调用层次,游戏的控制、制作与修改都非常方便。为了发射子弹,在最后一层的“敌人移动动画”中,用适当的帧调用发射子弹的程序:
_root.fire_big_enemy_bullet(enemy1_main_x, enemy1_main_y, 270);
以下程序表示敌人向下发射子弹。其函数代码如下:
function fire_big_enemy_bullet (place_x, place_y, angle) {
// 敌人发射有一定角度的子弹
big_temp = _root.big_now_bullet++;
if (big_temp>_root.deplicate[9]-_root.deplicate[8]) {
big_temp -= _root.deplicate[9]-_root.deplicate[8]+1;
}
new_name = "big_enemy_bullet_"+big_temp;
attachMovie("enemy_bullet", new_name, big_temp+_root.deplicate[8]);
//敌人子弹的位置等于前面所在的位置
_root[new_name]._x = place_x;
_root[new_name]._y = place_y;
//定义每次子弹移动的距离,这个移动距离就可以控制子弹的飞行方向了
_root[new_name].x_move = _root.cs[angle-1]*20;
_root[new_name].y_move = _root.sn[angle-1]*20;
_root.big_now_bullet = big_temp+1;
}
在这里,通过一个sin函数与cos函数来计算出每次子弹移动的距离。为了加快程序执行的速度,在系统初始化时就先把sin与cos的函数值存在一个360级的数组中。在使用时就不需要再调用慢速的三角函数了。那为什么不用定时器来完成定时发射子弹的功能,而采用直接在相应的帧调用发射子弹的函数呢?原因很简单,加入定时器会增加程序的运算量,当敌人“死亡”之后还要清除定时器,这实在太麻烦了。而直接在帧中加入代码程序其运行速度是最快的。对于只发射几次子弹的移动,根本不需要定时器来完成。
2.按特定路径运动的敌人
如果一个游戏只有直线飞行的敌人,那么这个游戏肯定没有人愿意玩,因此游戏中很重要的一点就是要设置敌人按特定的有规律的方式运动。这时才可真正理解到前面为什么一定要让程序与动画各自独立。因为敌人飞行的路径可以千变万化,如果全部用程序来模拟这个运行路线,将是一个非常可怕的工作量。值得庆幸的是,Flash提供了按路径来移动物体的功能,利用这个功能,就可以让敌人以任何方式运动了。游戏中设定某一种敌人在被打死之后可以放出加血令牌与加火力令牌,为了增加游戏的难度,这种敌人不能被玩家轻易地击毙,因此,在运行轨迹方面要让敌人来回摆动,如图6-16所示。

图6-16
这样,我的飞船就难以打到敌人了。而在动作的同时可以再发射一些子弹,如图6-17所示。

图6-17
如图6-17中所示的第11帧、第23帧、第37帧的程序用于发射子弹,它们的调用方式如下:
_root.enemy_fire(enemy2_main_x,enemy2_main_y)
发射子弹的程序如下:
function enemy_fire (place_x, place_y) {
// 敌人发射子弹的程序,散弹!
for (var j = 0; j<_root.enemy_powder; j++) {
temp = _root.enemy_bullet++;
if (temp>_root.deplicate[5]-_root.deplicate[4]) {
temp -= _root.deplicate[3]-_root.deplicate[4]+1;
}
var new_name = "enemy_bullet_"+temp;
attachMovie("enemy_bullet", new_name, temp+_root.deplicate[4]);
_root[new_name]._x = place_x;
_root[new_name]._y = place_y;
angle = 270+(j-1.0-_root.enemy_pwder/2)*_root.angle_between;
_root[new_name].x_move = _root.cs[angle-1]*20;
_root[new_name].y_move = _root.sn[angle-1]*20;
}
_root.enemy_bullet = temp+1;
}
这个程序发射出来的子弹函数是一种散弹,方向是270°方向(向下),子弹的数量等于_root.enemy_powder。在游戏初始化时设定其为3,也就是说放令牌的敌人一次可以发射3颗子弹。
这个放令牌的敌人的程序与前面的直线运动敌人的程序基本上一样,只有其血量值及下面这条语句不同:
_parent._parent.enemy2_main_x = _parent._x+_parent._parent._x;
_parent._parent.enemy2_main_y=_parent._y+_parent._parent._y+_parent.half_height;
这里将变量名与直线运动区分开是为了区分不同的物体,区分不同的敌人,从而保证每个敌人发射的子弹仅跟自己位置有关。在敌人爆炸的那个帧里放上下述加血令牌语句:
_root.put_blood(_parent.enemy2_main_x,_parent.enemy2_main_y-_parent.half_height)
或者放武器威力令牌:
_root.put_pai(_parent.enemy2_main_x,_parent.enemy2_main_y-_parent.half_height)
3.敌人的子弹
在游戏中还有两种其他种类的子弹、爆炸弹和跟踪弹。
爆炸弹程序如下:
function bomb_fire (place_x, place_y, bomb_between) {
// 爆炸弹程序
angle = 0;
for (var j = 0; j<360/bomb_between; j++) {
var bomb_temp = _root.bomb_enemy_bullet++;
if (bomb_temp>_root.deplicate[14]-_root.deplicate[13]) {
bomb_temp -= _root.deplicate[14]-_root.deplicate[13]+1;
}
var new_name = "bomb_bullet_"+bomb_temp;
attachMovie("bomb_bullet", new_name, bomb_temp+_root.deplicate[13]);
_root[new_name]._x = place_x;
_root[new_name]._y = place_y;
angle += bomb_between;
_root[new_name].x_move = _root.cs[angle-1]*20;
_root[new_name].y_move = _root.sn[angle-1]*20;
}
_root.bomb_enemy_bullet = bomb_temp+1;
}
between是每个子弹间隔的角度。比如说其值等于40,那么一次产生9颗子弹,呈球形状分布。显然between越小,子弹数量越多,子弹就越密集。通过这个程序可以轻松地控制产生出呈放射状任意颗子弹的爆炸弹。经过实际运行,这个爆炸弹效果在游戏产生了很好的效果,让玩家有极大的快感!
跟踪弹程序如下:
function fire_ai_bullet(place_ai_x, place_ai_y) {
// 发射会跟踪的子弹程序
_root.ai_enemy_bullet++;
var ai_new_name = "blue_bullet_"+_root.ai_enemy_bullet;
attachMovie("blue_bullet",ai_new_name,_root.ai_enemy_bullet+_root.deplicate[7]);
_root[ai_new_name]._x = place_ai_x;
_root[ai_new_name]._y = place_ai_y;
// once_speed表示子弹每次移动的距离
var once_speed = 16;
var ai_temp_x = place_ai_x-_root.my_plane._x;
ai_angle=180+Math.round(Math.atan2(place_ai_y-_root.my_plane._y, ai_temp_x) /_root.rad);
_root[ai_new_name].move_x = _root.cs[ai_angle-1]*once_speed;
_root[ai_new_name].move_y = _root.sn[ai_angle-1]*once_speed;
}
这里的跟踪子弹是最简单的跟踪弹。就是敌人判断我的飞船的位置后,向我所在的中心位置发射的子弹。但是如果我马上移开,就不会被打中了。这里的角度计算代码ai_angle=180…中加上180°是因为屏幕的坐标系与在纸上画出来的坐标是相反的,也就是说y轴的正轴在屏幕上是向下的,所以要平移180°。同时也保证(对应Sin和Cos)在数组的范围之内。采用Math.atan2函数求出来的角度在-180°至180°范围之内,不像Math.atan函数那样其值域在-90°至90°范围之内。这样的话,如果将180°值域范围转化成360°的值域范围,程序将变得复杂。