从零开始学AS3游戏开发【二】 随机移动的敌人

注:本系列教程每周一篇,旨在引导刚刚接触FLASH的新手通过实例进行游戏开发的学习。在过程中逐步说明涉及到的类及对应的使用方法。从一个光秃秃的方块开始,根据不同的控制方式、玩法产生不同的分支,最终完善成一个个可玩的游戏。希望对各位入门的朋友有所帮助!在教程涉及的各种处理方法,可能不够完善,也希望各位高手指正:)

转载请注名来源于天地会

第二篇 随机移动的敌人

上一篇教程中,我们搭建了一个简单的程序结构,并编写了程序,通过键盘的控制,让游戏场景中的一个方块根据我们的要求移动。对于一个游戏来讲,只有主角是不够的,我们需要敌人。

敌人和主角拥有相同的属性:他们都有长相(外表,虽然现在只是一个方块。。。),有控制器进行控制。所以,我们可以从ActionObject来进行扩展,产生两个不同的分支来实现他们。我们先来构建整个程序的结构:

可以看出,ActionObject是不具备“长相”这个特征的,而不管是主角或敌人,都有自己不同的样式。在上一篇的教程中,我们把黑色的方框直接指定为了ActionObject类,这样扩展性显然不好。因此,我们单独建立一个具备长相特征的类,继承自ActionObject。具体的操作方法可以参考上一篇教程。注意,因为ActionObject的构造函数是有参数的,所以我们勾选匹配基类的构造函数,让FD自动帮我们生成构造函数。
1.jpg

  1. package D5Power.Objects 
  2. {
  3.         import flash.display.Sprite;
  4.         import D5Power.Controller.basicController;
  5.         /**
  6.          * 具备外表皮肤的ActionObject
  7.          * @author D5Power
  8.          */
  9.         public class FaceObject extends ActionObject
  10.         {
  11.                 protected var _face:Sprite;
  12.                 /**
  13.                  * 
  14.                  * @param        ctrl        控制器
  15.                  * @param        face        外观
  16.                  */
  17.                 public function FaceObject(ctrl:basicController,face:Sprite) 
  18.                 {
  19.                         super(ctrl);
  20.                         _face = face;
  21.                         addChild(_face);
  22.                 }
  23.                 
  24.         }
  25. }
复制代码

FaceObject在ActionObject的基础上,增加了_face属性,并在构造函数中,通过addChild把_face显示了出来,这样,我们就可以给他赋予不同的外观了。

接下来来实现主角类和敌人类。继承自我们刚刚写好的FaceObject类。暂时直接继承就可以了,不用写其他的代码。

  1. package D5Power.Objects 
  2. {
  3.         import flash.display.Sprite;
  4.         import D5Power.Controller.basicController;
  5.         /**
  6.          * 主角类
  7.          * @author D5Power
  8.          */
  9.         public class Player extends FaceObject
  10.         {
  11.                 
  12.                 public function Player(ctrl:basicController, face:Sprite) 
  13.                 {
  14.                         super(ctrl, face);
  15.                         
  16.                 }
  17.                 
  18.         }
  19. }
复制代码
  1. package D5Power.Objects 
  2. {
  3.         import flash.display.Sprite;
  4.         import D5Power.Controller.basicController;
  5.         /**
  6.          * 敌人类
  7.          * @author D5Power
  8.          */
  9.         public class Monster extends FaceObject
  10.         {
  11.                 
  12.                 public function Monster(ctrl:basicController, face:Sprite) 
  13.                 {
  14.                         super(ctrl, face);
  15.                         
  16.                 }
  17.                 
  18.         }
  19. }
复制代码

既然结构改变了,那我们在上一篇教程中使用的黑色方块就没必要再定义为ActionObject了,我们现在只把它作为皮肤使用,在库中右键选择,在弹出菜单中选择属性,把类名改为Skin1。由于我们并没有编写Skin1,FLASH会帮我们自动生成一个Skin1类,以后就可以通过new Skin1()来进行声明了。用同样的办法,我们再重新绘制一个红色的方块,作为敌人的皮肤。定义类名为Skin2

2.jpg 

而主场景中,我们原来的代码就需要做一下修改了:

  1. ...
  2. var obj:Player = new Player(ctrl,new Skin1());
  3. ...
复制代码

当然,也可以试一下用敌人的皮肤:

  1. ...
  2. var obj:Player = new Player(ctrl,new Skin2());
  3. ...
复制代码

这样,大体的结构就已经建好了。接下来。我们来针对问题进行不同的处理。

首先,既然敌人和主角都可以移动。那么,我们就有必要增加限制。因为它们势必只能在允许的范围内移动。就我们目前的需求来看,他们只能在屏幕的区域范围内移动(因为我们暂时没有任何障碍物)。因此,在移动前,我们要检查它的下一个目标点是否可以移动。很显然,这个检查是同时针对敌人和主角的,因此,我们在ActionObject上进行扩展,修改ActionObject的代码,增加nextCanMove方法:

  1. /**
  2.                  * 下一目标点是否可以移动
  3.                  */
  4.                 public function get nextCanMove():Boolean
  5.                 {
  6.                         // 下一X位置
  7.                         var nx:uint = 0;
  8.                         // 下一Y位置
  9.                         var ny:uint = 0;
  10.                         // 根据移动方向进行处理,计算出下一目标点位置
  11.                         switch(walkDirection)
  12.                         {
  13.                                 case UP:
  14.                                         ny = y-speed;
  15.                                         break;
  16.                                 case DOWN:
  17.                                         ny = y+speed;
  18.                                         break;
  19.                                 case LEFT:
  20.                                         nx = x-speed;
  21.                                         break;
  22.                                 case RIGHT:
  23.                                         nx = x+speed;
  24.                                         break;
  25.                                 default:break;
  26.                         }
  27.                         
  28.                         // 如果下一目标点超出屏幕范围,则不能移动
  29.                         if (nx > Global.stage.stageWidth - width || nx < 0) return false;
  30.                         if (ny > Global.stage.stageHeight - height || ny < 0) return false;
  31.                         
  32.                         // 检测通过
  33.                         return true;
  34.                 }
复制代码

同时,修改move方法:

  1. /**
  2.                  * 移动
  3.                  */
  4.                 protected function move():void
  5.                 {
  6.                         if (!nextCanMove) return;  // 增加了这句代码
  7.                         // 根据不同的方向进行移动
  8.                         switch(walkDirection)
  9.                         {
  10.                                 case UP:
  11.                                         y -= speed;
  12.                                         break;
  13.                                 case DOWN:
  14.                                         y += speed;
  15.                                         break;
  16.                                 case LEFT:
  17.                                         x -= speed;
  18.                                         break;
  19.                                 case RIGHT:
  20.                                         x += speed;
  21.                                         break;
  22.                                 default:break;
  23.                         }
  24.                 }
复制代码

这样,只有下一目标点可以移动的前提下,才能进行移动。移动限制的问题解决了。

其次。敌人是可以自动移动的,而不是受键盘的控制。那么控制器应该可以自己运行,所以,我们扩展basicController,增加空的AutoRun方法

  1. /**
  2.                  * 自动运行
  3.                  */
  4.                 public function AutoRun():void{}
复制代码

之后,在需要实现自动运行的控制器里覆盖它来实现具体的功能就可以了。既然我们要敌人随机移动,那实际上也就是随机地给于敌人移动方向。因此我们来修改MonsterController控制器,增加随机修改方向的方法:

  1. /**
  2.                  * 随机修改方向
  3.                  */
  4.                 private function changeDir():void
  5.                 {
  6.                         var me:ActionObject = _target as ActionObject;
  7.                         me.direction = 1+int(Math.random() * 4);
  8.                 }
复制代码

我们知道,MonsterController的控制对象肯定是ActionObject(Monster是它的子类),因此,我们把_target转换成ActionObject以获得direction属性。Math.random()将随机产生一个0-1的小数(注意,有可能为0,但永远都不会取到1),我们把他乘以4,就获得了0-4的一个随机数(0-3.99999999...)。把他转换为整型,实际上我们随机取得了0-3.而我们的方向是1-4,因此,在这个基础上+1,即可随机获得一个正确的方向值。

接下来,我们覆盖实现AutoRun方法,继续修改MonsterController控制器:

  1. override public function AutoRun():void
  2.                 {
  3.                         var me:ActionObject = _target as ActionObject;
  4.                         if (!me.nextCanMove) changeDir();
  5.                 }
复制代码

同样,把_target转换成ActionObject,如果他的下一个移动点无法移动了,则自动转换移动方向,这个方向是随机的。

小提示:你也可以把_target转换成Monster,如果Monster有属于自己的独特函数需要使用的话,不过,一定要确认你传递进来的的确是Monster类哦:)

再来看一下敌人类(Monster),我们在控制器里实现了对敌人的自动控制,但是,必须要他自动运行,也就是说,需要有地方来不停的调用AutoRun方法,来实现控制器根据不同的情况控制目标。来看一下代码:

  1. package D5Power.Objects 
  2. {
  3.         import flash.display.Sprite;
  4.         import D5Power.Controller.basicController;
  5.         
  6.         /**
  7.          * 敌人类
  8.          * @author D5Power
  9.          */
  10.         public class Monster extends FaceObject
  11.         {
  12.                 /**
  13.                  * 上一次的运行日期对象
  14.                  */
  15.                 private var _lastAction:Date;
  16.                 /**
  17.                  * 运行频率
  18.                  */
  19.                 private var _fps:uint = 8;
  20.                 
  21.                 public function Monster(ctrl:basicController, face:Sprite) 
  22.                 {
  23.                         super(ctrl, face);
  24.                         _lastAction = new Date();
  25.                 }
  26.                 
  27.                 override public function Do():void
  28.                 {
  29.                         var date:Date = new Date();
  30.                         // 如果运行时间已经超过频率所指定的时间间隔,那么运行程序
  31.                         if (date.time-_lastAction.time > 1000/_fps)
  32.                         {
  33.                                 _lastAction = date;
  34.                                 controller.AutoRun();
  35.                                 super.Do();
  36.                         }
  37.                 }
  38.                 
  39.         }
  40. }
复制代码

我们新增加了两个属性:_fps和_lastAction。_fps是运行的频率,也就是每秒内,所运行的次数,而_lastAction则是最后一次运行的日期对象。来看一下实现按频率运行的方法。每次Do函数运行的时候,会取一个新的Date对象,我们可以通过它的.time属性,来获取本次运行的毫秒时间(一个时间戳),把这个毫秒时间和上一次运行的毫秒时间做对比,如果超出了_fps所规定的运行时间,则运行程序,并把当前日期设置为最后一次运行的日期。在这里,我们调用可控制器的AutoRun方法。

这样,所有的前期准备都准备完了,我们回到主场景Main来实现实际的功能。


我们生成一个敌人,并把他放入列表:

  1. var ctrl2:MonsterControler = new MonsterControler();
  2.                         var monster:Monster = new Monster(ctrl2, new Skin2());
  3.                         scene.addObject(monster);
复制代码

这时,运行程序,我们可以看到场景里有了一红一黑两个方块,其中红色的将在场景内的边缘随机游荡。
3.jpg 

不过,主角和敌人在同一地点出现,这很难看的清楚,所以,我们可以进一步修改代码。把主角放到200,200的位置,而利用for循环,生成3个敌人,并随机放到场景中500*300这个范围内:

  1. var scene:gameScene = new gameScene(stage);                // 声明游戏舞台
  2.                         var ctrl:KeyController = new KeyController();        // 定义控制器
  3.                         var obj:Player = new Player(ctrl, new Skin1());
  4.                         obj.x = 200;
  5.                         obj.y = 200;
  6.                         scene.addObject(obj);// 将对象添加到舞台中
  7.                         
  8.                         for (var i:uint = 0; i < 3; i++)
  9.                         {
  10.                                 var ctrl2:MonsterControler = new MonsterControler();
  11.                                 var monster:Monster = new Monster(ctrl2, new Skin2());
  12.                                 monster.x = int(Math.random() * 500);
  13.                                 monster.y = int(Math.random() * 300);
  14.                                 scene.addObject(monster);
  15.                         }
复制代码

完整的源代码如下:

 Teach2.rar (13.95 KB) 

在下一篇教程里,我们将对现有的结构做一下整理,并让主角可以发射子弹攻击敌人。

原文地址:https://www.cnblogs.com/keng333/p/2304954.html