Flash游戏开发(14) - 愚蠢的敌人

愚蠢的敌人

我们的英雄已经很完美了,但是他很无聊,唯一能做的只是来回走。我们需要别的东西。不是食物,不是饮料,也不是美女,我们需要的是——一些敌人。敌人就像加在汤里的盐,缺了它,一切都索然无味。好的游戏中会有聪明的敌人,但是我们从一些最笨的敌人开始做起。他们所做的仅仅是来回走,顺便检测是不是碰上英雄了。


到目前为止,我们已经有了两类对象:英雄和方块。英雄由玩家操纵,方块不会运动。敌人应该类似英雄,唯一不同的是,我们不能操作他们移动,我们将会赋予他们一定的智能。我们将要做两种不同的敌人,第一种上下走,第二种会左右走。他们都会在撞到墙上后回头继续走。(真够笨的:)

在你开始构造你的超级复杂的敌人之前,再想想一些东西。许多游戏实际上没有用到敌人,有的虽然用到了,也不是很聪明。Flash并不是非常强大,如果你的游戏中有100个敌人,他们都聪明地使用A*算法跟踪英雄,我真的怀疑是否有这么强大的机器能运行。如果可以的话,最好让一些敌人愚蠢些,一些聪明些,结果玩家可能就会忽略了他们的差别。另外,我们都想要比别人聪明,所以就让玩家感觉这种乐趣好了 :)

准备敌人

同创建英雄一样,创建一个剪辑放置敌人(如果忘了怎么做,在回头看看)。他们也有4帧,分别是左、上、下、右的动画。同样的,他们也要导出为“enemy1”和“enemy2”(在库面板中设置linkage)。现在我们添加一个enemies数组:

myEnemies = [
[0],
[[1, 6, 1]],
[[2, 1, 3]]
];

看得出来,我们在map1放了1个敌人。
[1,6,1]:第一个1代表敌人的类型(hoho~我们有好多中敌人得)
6,1是他开始的位置。创建地图的时候,我们会把他放到x=6,y=1的方块上。同理在map2中也有一个敌人,但是类型是2,位置是1,3。你可在一个地图中放置多个敌人,但是千万记住,不要把他们嵌到墙里面!记得要放在一个可通行的方块上面。

让我们声明一些敌人的模板:

game.Enemyp1= function () {};
game.Enemyp1.prototype.xMove=0;
game.Enemyp1.prototype.yMove=1;
game.Enemyp1.prototype.speed=2;

game.Enemyp2= function () {};
game.Enemyp2.prototype.xMove=1;
game.Enemyp2.prototype.yMove=0;
game.Enemyp2.prototype.speed=2;

他们的代码看起来很相似,但是他们动作就不一样了。Enemyp1会上下走,因为他的yMove属性是1;但是Enemyp2只会水平移动。你可以设置xMove/yMove属性为1或-1或0。不过请不要同时把这两个属性都设置成非零值,除非你希望敌人能走对角线。

你也可以把xMove和yMove都设置成0,这样的敌人没有移动的能力,也许你会用到。

speed属性声明了敌人移动的速度。不同的敌人可以有不同的速度。

摆放敌人

在buildMap函数中,介于创建门和创建英雄的代码之间,加入如下代码:

var enemies = myEnemies[game.currentMap];
game.currentEnemies = enemies.length;
for (var i = 0; i<game.currentEnemies; ++i) {
  var name = "enemy"+i;
  game[name]= new game["Enemyp"+enemies[i][0]];
  game.clip.attachMovie("enemy"+enemies[i][0], name, 10001+i);
  game[name].clip=game.clip[name];
  game[name].xtile = enemies[i][1];
  game[name].ytile = enemies[i][2];
  game[name].width = game.clip[name]._width/2;
  game[name].height = game.clip[name]._height/2;
  game[name].x = (game[name].xtile *game.tileW)+game.tileW/2;
  game[name].y = (game[name].ytile *game.tileH)+game.tileH/2;
  game[name].clip._x = game[name].x;
  game[name].clip._y = game[name].y;
}

这段代码什么意思?首先我们得到当前地图的敌人数组,转存为enemies数组,然后把敌人的个数传给currentEnemies,接着遍历敌人数组,把他们都安置好。
(记住,虽然这里只用到了一个敌人,但是可以有更多的)

变量name的值是新创建的敌人的名字,“enemy0”,“enemy1”,“enemy2”……依次类推。然后我们从相应的模板(刚刚声明过)创建新的对象:

game[name]= new game["Enemy"+enemies[i][0]];

从敌人数组(enemies[i])的第一个元素(enemies[i][0])得到敌人的类型,比如是1,然后通过enemyp1模板创建一个敌人。

下面的几行是取得坐标,然后把敌人放置到该处。ok,这就是buildMap函数该变动的地方。

但是,你也许会大声喊出来,但是他还不会动!好吧,我们就让他动起来

移动敌人

同人一样,敌人也需要脑子,我们就写个enemyBrain函数好了:

function enemyBrain () {
for (var i = 0; i<game.currentEnemies; ++i) {
  var name = "enemy"+i;
  var ob = game[name];
  getMyCorners (ob.x+ob.speed*ob.xMove, ob.y+ob.speed*ob.yMove, ob);
  if (ob.downleft and ob.upleft and ob.downright and ob.upright) {
    moveChar(ob, ob.xMove, ob.yMove);
  } else {
    ob.xMove = -ob.xMove;
    ob.yMove = -ob.yMove;
  }
  var xdist = ob.x - char.x;
  var ydist = ob.y - char.y;
  if (Math.sqrt(xdist*xdist+ydist*ydist) < ob.width+char.width) {
    removeMovieClip(_root.tiles);
    _root.gotoAndPlay(1);
  }
}
}

正如你所看到的,我们又要遍历数组了。我们把enemies数组的每个元素存到ob,当i等于0的时候,ob就是enemy0。

然后我们调用getCorners函数,检查敌人是不是碰到了障碍物,如果upleft、downleft、upright、downright都是true,则说明可以行走。我们就可以放心的调用moveChar函数,同时传递xMove、yMove给moveChar函数。我们以前用moveChar都是移动英雄的,现在移动敌人也没有任何区别,我说过,我们可以重复使用许多函数的。

如果英雄碰到了障碍物,我们就让xMove和yMove都取相反数,比如原来xMove是1,就取-1,这样敌人就会掉头移动。如果yMove原来是0,那么相反数还是0,没有影响。

最后一部分检测英雄和敌人的距离,看是不是碰到了一起,如果是,oh,game over。当然,游戏不可能这么简单的结束掉,你可以减少英雄的生命值或者做别的处理,这些都由你自己完成。我们使用的计算距离的公式用的是“勾股定理”,如果你需要精确到象素级别的接触,你应该用hitTest,但是这里没有必要这么精确。别离敌人太近,否则你会死掉的。:-)

我们需要不停的调用这个函数,所以在detectKeys函数中加入:

_root.enemyBrain();

这就是一个敌人,很愚蠢的敌人。接下来我们要做更聪明的敌人,他们象人一样四处巡逻,碰到障碍物后会改变方向继续巡逻。hoho,继续吧~

评论