zoukankan      html  css  js  c++  java
  • 从零开始学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) 

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

  • 相关阅读:
    TSQL 字符串前加 N 是什么意思
    SQL语句修改字段默认值
    Clustering Technologies On Windows Server 2008 R2
    sql 修改列名及表名
    Capture screenshots on Mac 在Mac下截图
    极限编程(XP,eXtreme Programming)
    zookeeper安装(单机版)
    linux备份用户权限
    Dubbo管控台安装(zookeeper单机版)
    linux查看IP
  • 原文地址:https://www.cnblogs.com/keng333/p/2304954.html
Copyright © 2011-2022 走看看