注:本系列教程每周一篇,旨在引导刚刚接触FLASH的新手通过实例进行游戏开发的学习。在过程中逐步说明涉及到的类及对应的使用方法。从一个光秃秃的方块开始,根据不同的控制方式、玩法产生不同的分支,最终完善成一个个可玩的游戏。希望对各位入门的朋友有所帮助!在教程涉及的各种处理方法,可能不够完善,也希望各位高手指正:)
转载请注名来源于天地会。
第二篇 随机移动的敌人
在上一篇教程中,我们搭建了一个简单的程序结构,并编写了程序,通过键盘的控制,让游戏场景中的一个方块根据我们的要求移动。对于一个游戏来讲,只有主角是不够的,我们需要敌人。
敌人和主角拥有相同的属性:他们都有长相(外表,虽然现在只是一个方块。。。),有控制器进行控制。所以,我们可以从ActionObject来进行扩展,产生两个不同的分支来实现他们。我们先来构建整个程序的结构:
可以看出,ActionObject是不具备“长相”这个特征的,而不管是主角或敌人,都有自己不同的样式。在上一篇的教程中,我们把黑色的方框直接指定为了ActionObject类,这样扩展性显然不好。因此,我们单独建立一个具备长相特征的类,继承自ActionObject。具体的操作方法可以参考上一篇教程。注意,因为ActionObject的构造函数是有参数的,所以我们勾选匹配基类的构造函数,让FD自动帮我们生成构造函数。
- package D5Power.Objects
- {
- import flash.display.Sprite;
- import D5Power.Controller.basicController;
- /**
- * 具备外表皮肤的ActionObject
- * @author D5Power
- */
- public class FaceObject extends ActionObject
- {
- protected var _face:Sprite;
- /**
- *
- * @param ctrl 控制器
- * @param face 外观
- */
- public function FaceObject(ctrl:basicController,face:Sprite)
- {
- super(ctrl);
- _face = face;
- addChild(_face);
- }
-
- }
- }
复制代码
FaceObject在ActionObject的基础上,增加了_face属性,并在构造函数中,通过addChild把_face显示了出来,这样,我们就可以给他赋予不同的外观了。
接下来来实现主角类和敌人类。继承自我们刚刚写好的FaceObject类。暂时直接继承就可以了,不用写其他的代码。
- package D5Power.Objects
- {
- import flash.display.Sprite;
- import D5Power.Controller.basicController;
- /**
- * 主角类
- * @author D5Power
- */
- public class Player extends FaceObject
- {
-
- public function Player(ctrl:basicController, face:Sprite)
- {
- super(ctrl, face);
-
- }
-
- }
- }
复制代码
- package D5Power.Objects
- {
- import flash.display.Sprite;
- import D5Power.Controller.basicController;
- /**
- * 敌人类
- * @author D5Power
- */
- public class Monster extends FaceObject
- {
-
- public function Monster(ctrl:basicController, face:Sprite)
- {
- super(ctrl, face);
-
- }
-
- }
- }
复制代码
既然结构改变了,那我们在上一篇教程中使用的黑色方块就没必要再定义为ActionObject了,我们现在只把它作为皮肤使用,在库中右键选择,在弹出菜单中选择属性,把类名改为Skin1。由于我们并没有编写Skin1,FLASH会帮我们自动生成一个Skin1类,以后就可以通过new Skin1()来进行声明了。用同样的办法,我们再重新绘制一个红色的方块,作为敌人的皮肤。定义类名为Skin2
而主场景中,我们原来的代码就需要做一下修改了:
- ...
- var obj:Player = new Player(ctrl,new Skin1());
- ...
复制代码
当然,也可以试一下用敌人的皮肤:
- ...
- var obj:Player = new Player(ctrl,new Skin2());
- ...
复制代码
这样,大体的结构就已经建好了。接下来。我们来针对问题进行不同的处理。
首先,既然敌人和主角都可以移动。那么,我们就有必要增加限制。因为它们势必只能在允许的范围内移动。就我们目前的需求来看,他们只能在屏幕的区域范围内移动(因为我们暂时没有任何障碍物)。因此,在移动前,我们要检查它的下一个目标点是否可以移动。很显然,这个检查是同时针对敌人和主角的,因此,我们在ActionObject上进行扩展,修改ActionObject的代码,增加nextCanMove方法:
- /**
- * 下一目标点是否可以移动
- */
- public function get nextCanMove():Boolean
- {
- // 下一X位置
- var nx:uint = 0;
- // 下一Y位置
- var ny:uint = 0;
- // 根据移动方向进行处理,计算出下一目标点位置
- switch(walkDirection)
- {
- case UP:
- ny = y-speed;
- break;
- case DOWN:
- ny = y+speed;
- break;
- case LEFT:
- nx = x-speed;
- break;
- case RIGHT:
- nx = x+speed;
- break;
- default:break;
- }
-
- // 如果下一目标点超出屏幕范围,则不能移动
- if (nx > Global.stage.stageWidth - width || nx < 0) return false;
- if (ny > Global.stage.stageHeight - height || ny < 0) return false;
-
- // 检测通过
- return true;
- }
复制代码
同时,修改move方法:
- /**
- * 移动
- */
- protected function move():void
- {
- if (!nextCanMove) return; // 增加了这句代码
- // 根据不同的方向进行移动
- switch(walkDirection)
- {
- case UP:
- y -= speed;
- break;
- case DOWN:
- y += speed;
- break;
- case LEFT:
- x -= speed;
- break;
- case RIGHT:
- x += speed;
- break;
- default:break;
- }
- }
复制代码
这样,只有下一目标点可以移动的前提下,才能进行移动。移动限制的问题解决了。
其次。敌人是可以自动移动的,而不是受键盘的控制。那么控制器应该可以自己运行,所以,我们扩展basicController,增加空的AutoRun方法
- /**
- * 自动运行
- */
- public function AutoRun():void{}
复制代码
之后,在需要实现自动运行的控制器里覆盖它来实现具体的功能就可以了。既然我们要敌人随机移动,那实际上也就是随机地给于敌人移动方向。因此我们来修改MonsterController控制器,增加随机修改方向的方法:
- /**
- * 随机修改方向
- */
- private function changeDir():void
- {
- var me:ActionObject = _target as ActionObject;
- me.direction = 1+int(Math.random() * 4);
- }
复制代码
我们知道,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控制器:
- override public function AutoRun():void
- {
- var me:ActionObject = _target as ActionObject;
- if (!me.nextCanMove) changeDir();
- }
复制代码
同样,把_target转换成ActionObject,如果他的下一个移动点无法移动了,则自动转换移动方向,这个方向是随机的。
小提示:你也可以把_target转换成Monster,如果Monster有属于自己的独特函数需要使用的话,不过,一定要确认你传递进来的的确是Monster类哦:)
再来看一下敌人类(Monster),我们在控制器里实现了对敌人的自动控制,但是,必须要他自动运行,也就是说,需要有地方来不停的调用AutoRun方法,来实现控制器根据不同的情况控制目标。来看一下代码:
- package D5Power.Objects
- {
- import flash.display.Sprite;
- import D5Power.Controller.basicController;
-
- /**
- * 敌人类
- * @author D5Power
- */
- public class Monster extends FaceObject
- {
- /**
- * 上一次的运行日期对象
- */
- private var _lastAction:Date;
- /**
- * 运行频率
- */
- private var _fps:uint = 8;
-
- public function Monster(ctrl:basicController, face:Sprite)
- {
- super(ctrl, face);
- _lastAction = new Date();
- }
-
- override public function Do():void
- {
- var date:Date = new Date();
- // 如果运行时间已经超过频率所指定的时间间隔,那么运行程序
- if (date.time-_lastAction.time > 1000/_fps)
- {
- _lastAction = date;
- controller.AutoRun();
- super.Do();
- }
- }
-
- }
- }
复制代码
我们新增加了两个属性:_fps和_lastAction。_fps是运行的频率,也就是每秒内,所运行的次数,而_lastAction则是最后一次运行的日期对象。来看一下实现按频率运行的方法。每次Do函数运行的时候,会取一个新的Date对象,我们可以通过它的.time属性,来获取本次运行的毫秒时间(一个时间戳),把这个毫秒时间和上一次运行的毫秒时间做对比,如果超出了_fps所规定的运行时间,则运行程序,并把当前日期设置为最后一次运行的日期。在这里,我们调用可控制器的AutoRun方法。
这样,所有的前期准备都准备完了,我们回到主场景Main来实现实际的功能。
我们生成一个敌人,并把他放入列表:
- var ctrl2:MonsterControler = new MonsterControler();
- var monster:Monster = new Monster(ctrl2, new Skin2());
- scene.addObject(monster);
复制代码
这时,运行程序,我们可以看到场景里有了一红一黑两个方块,其中红色的将在场景内的边缘随机游荡。
不过,主角和敌人在同一地点出现,这很难看的清楚,所以,我们可以进一步修改代码。把主角放到200,200的位置,而利用for循环,生成3个敌人,并随机放到场景中500*300这个范围内:
- var scene:gameScene = new gameScene(stage); // 声明游戏舞台
- var ctrl:KeyController = new KeyController(); // 定义控制器
- var obj:Player = new Player(ctrl, new Skin1());
- obj.x = 200;
- obj.y = 200;
- scene.addObject(obj);// 将对象添加到舞台中
-
- for (var i:uint = 0; i < 3; i++)
- {
- var ctrl2:MonsterControler = new MonsterControler();
- var monster:Monster = new Monster(ctrl2, new Skin2());
- monster.x = int(Math.random() * 500);
- monster.y = int(Math.random() * 300);
- scene.addObject(monster);
- }
复制代码
完整的源代码如下:
Teach2.rar (13.95 KB)
在下一篇教程里,我们将对现有的结构做一下整理,并让主角可以发射子弹攻击敌人。