从零开始学AS3游戏开发【一】 响应键盘移动的方块

本帖最后由 D5Power 于 2010-10-2 23:13 编辑

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

转载请注名来源于天地会

第一篇 响应键盘事件的方块

准备工作:FLASH CS3/4/5,FlashDevelop(http://www.flashdevelop.org/community/viewforum.php?f=11
开发目标:实现一个受键盘控制的方块
相关API:Sprite,KeyboradEvent

我们即将开始构建一个游戏世界……
任何属于这个世界的物体,我们都叫它游戏对象(gameObject)。它是游戏的基本元素,所有的一切都是从它衍生而来。有的游戏对象可以被我们操作,因此,我们需要分配给它一个控制器(controller),并通过这个装置来控制它。我们一次不可能看遍全部世界,所以,我们必须有一个舞台(gameScene),让我们想看到的游戏对象来到这个舞台上表演。所有的一切。都通过这个舞台开始……

首先,做一下准备工作:

首先打开FlashDevelop,新建一个项目
01.jpg 

选择FLASH IDE项目,选择一个保存目录,并选择一个你喜欢的名字。
02.jpg 

在项目名上点右键,选择Add -> New Class,输入文件名Main,选择base Class后面的Browse按钮,输入Sprite
04.jpg 

这样,我们就建立了一个继承自Sprite类的对象Main。Sprite是FLASH的一个基本显示元素,它没有时间轴的概念。

打开FLASH CS3,新建一个ActionScript 3.0文件,并把它保存到刚才建立的项目的目录中。在属性栏里,把帧频改成30,文档类输入Main。
05.jpg

这样,我们就建立了一个可以由Main.as控制的FLASH文件。把刚才生成的Main.as改成像下面这样:
  1. package  
  2. {
  3.         import flash.display.Sprite;
  4.         public class Main extends Sprite
  5.         {
  6.                 
  7.                 public function Main() 
  8.                 {
  9.                         trace('我运行咯!');
  10.                 }
  11.                 
  12.         }
  13. }
复制代码
回到FLASH CS里,按下ctrl+enter进行测试:

06.jpg 

看到输出框里出现的输出,说明程序已经可以运行了。trace是FLASH中的消息输出命令,我们在今后的开发过程中会经常使用的。至此,准备工作完成。开始我们的创建之旅吧。

首先来建立游戏时间里的“亚当”——gameObject。在我们的项目里新建文件夹D5Power(起个自己喜欢的名字),并在D5Power下面再建一个文件夹Objects,最后,在Ojbects里Add一个New Class,输入文件名gameObject,同样,base class选择Sprite。建立完成后,我们的项目看起来应该像这样了:

07.jpg 

在FlashDevelop给我们生成的代码中,我们看到了package D5Power.Objects 这样的字眼。package即是包,我们的源代码可以存放在不同的包中,这样可以保持代码的结构清晰。包的名字实际上就是对应目录的名字。如package D5Power.Objects 这样,我们就可以知道gameObject文件是保存在D5Power/Objects/这个目录下面,如果package后面没有任何内容(比如我们刚才建立的Main),那么这个文件就是在项目的默认包(源代码的顶级目录)中。不同层级的包之间的访问,必须通过improt进行包含,而同级,以及访问默认包内的文件,是不需要的。这个我们以后会再继续详细的说明

回来考虑一下我们的“亚当”——gameObject,它是最基础的元素,所以,我们只给他定义最简单的可以做的事情,首先,他可以做一些事(至于做什么,我们先不考虑),另外,他会死亡。既然他可以产生在这个游戏世界里,那么他就必须可以从这个世界里消失的无影无踪:
  1. package D5Power.Objects 
  2. {
  3.         import flash.display.Sprite;
  4.         
  5.         /**
  6.          * 基础游戏对象
  7.          * @author D5Power
  8.          */
  9.         public class gameObject extends Sprite
  10.         {
  11.                 
  12.                 public function gameObject() 
  13.                 {
  14.                         
  15.                 }
  16.                 /**
  17.                  * 做些什么
  18.                  */
  19.                 public function Do():void { }
  20.                 /**
  21.                  * 死亡
  22.                  */
  23.                 public function die():void{}
  24.                 
  25.         }
  26. }
复制代码
我们定义了一个空的die函数,它什么也没做,我们将在以后慢慢的去补足它。现在,gameObject除了活着,就是死了。这显然不能满足我们的要求。所以我们必须让他繁衍下一代,并产生进化。在Objects里再新建一个类,命名为actionObject,把它的base class输入gameObject。现在,我们获得了一个“亚当的儿子”。他继承了亚当的全部特性(可以死亡),并可以有自己独特的事情可以做。

我们决定教他如何行走:
  1. package D5Power.Objects 
  2. {
  3.         /**
  4.          * 可活动的游戏对象
  5.          * @author D5Power
  6.          */
  7.         public class ActionObject extends gameObject
  8.         {
  9.                 /**
  10.                  * 移动速度
  11.                  */
  12.                 protected var speed:Number = 1.2;
  13.                 /**
  14.                  * 移动方向
  15.                  */
  16.                 protected var walkDirection:uint=0
  17.                 
  18.                 public static const UP:uint = 1;
  19.                 public static const DOWN:uint = 2;
  20.                 public static const LEFT:uint = 3;
  21.                 public static const RIGHT:uint = 4;
  22.                 
  23.                 public function ActionObject() 
  24.                 {
  25.                         
  26.                 }
  27.                 /**
  28.                  * 修改移动方向
  29.                  */
  30.                 public function set direction(dir:uint):void
  31.                 {
  32.                         walkDirection = dir;
  33.                 }
  34.                 /**
  35.                  * 移动
  36.                  */
  37.                 protected function move():void
  38.                 {
  39.                         // 根据不同的方向进行移动
  40.                         switch(walkDirection)
  41.                         {
  42.                                 case UP:
  43.                                         y -= speed;
  44.                                         break;
  45.                                 case DOWN:
  46.                                         y += speed;
  47.                                         break;
  48.                                 case LEFT:
  49.                                         x -= speed;
  50.                                         break;
  51.                                 case RIGHT:
  52.                                         x += speed;
  53.                                         break;
  54.                                 default:break;
  55.                         }
  56.                 }
  57.                 
  58.                 /**
  59.                  * 覆盖父类的Do方法
  60.                  */
  61.                 override public function Do():void
  62.                 {
  63.                         if (walkDirection != 0) move();
  64.                         super.Do();
  65.                 }
  66.         }
  67. }
复制代码
在这段代码中,我们第一次接触到了一些关键字,下面对他们进行说明
var FLASH中定义变量用的前缀,比如var num:int = 5;定义一个整型变量num,默认值为5
function FLASH中定义函数的前缀,比如 function test():void{} 定义一个名为test的函数,没有返回值
public 这个关键字之后的定义是公有的,可以被外部的类访问。比如,大家都知道你的名字,所以你的名字是public的。
private 这个关键字之后的定义是完全私有的,只能被类自己访问。比如,你银行的密码只有你自己知道,所以他是private的。
protected 这个关键字之后的定义,是私有的,但家族成员可以访问。比如,你儿子知道你屁股上有一颗痣,其他人不知道。。。笑,所以这个痣是protected的
override 覆盖父级类的定义
set 在上例代码中,可以通过某actionObject.direction这样来访问这个函数,而不是.direction(),和它类似的还有get关键字。你可以用他们来生成一个只读或只写的“变量”

现在,“亚当的儿子”可以行走了。但是,我们不知道怎样才能通知他走,怎样走。我们必须有一个控制他的方法,让他来听从我们的指挥。因为,我们必须创建一个控制器来控制它。在这之前,我们要学习一些操作这个世界的方法和技巧(上帝也需要学习么,呵呵)
首先,我们必须能在任何的地方都可以访问到主场景的FLASH舞台(Stage,你可以把他理解成我们在FLASH CS里可以看到的那个白白的工作区,呵呵).这样方便我们的操作。刚才我们说过,不管任何一个包,都可以访问到默认包里的东西。因此,我们在默认包里建立一个全局都可以访问的类Global,并用它来存放一些需要全局调用的东西:
  1. package  
  2. {
  3.         import flash.display.Stage;
  4.         /**
  5.          * 全局通用类
  6.          * @author D5Power
  7.          */
  8.         public class Global
  9.         {
  10.                 /**
  11.                  * 直接调用FLASH舞台
  12.                  */
  13.                 public static var stage:Stage;
  14.                 public function Global() 
  15.                 {
  16.                 }
  17.         }
  18. }
复制代码
之后,我们来创建控制器。在D5Power/下面新建一个目录Controller,用来存放控制器。和gameObject的结构一样,我们也建立一个最基础的控制器(basicController),其他控制器由它来衍生:
  1. package D5Power.Controller 
  2. {
  3.         /**
  4.          * 基础控制器
  5.          * @author D5Power
  6.          */
  7.         public class basicController
  8.         {
  9.                 /**
  10.                  * 控制对象
  11.                  */
  12.                 protected var _target:gameObject;
  13.                 
  14.                 public function basicController() 
  15.                 {
  16.                         
  17.                 }
  18.                 /**
  19.                  * 设置控制对象
  20.                  */
  21.                 public function set target(obj:gameObject):void
  22.                 {
  23.                         _target = obj;
  24.                 }
  25.                 /**
  26.                  * 消亡
  27.                  */
  28.                 public function die():void{}
  29.         }
  30. }
复制代码
之后,再来创建专门针对键盘控制的控制器KeyController,当然,继承自basicController:
  1. package D5Power.Controller 
  2. {
  3.         import D5Power.Objects.ActionObject;
  4.         import D5Power.Objects.gameObject;
  5.         import flash.events.KeyboardEvent;
  6.         /**
  7.          * 键盘控制器
  8.          * @author D5Power
  9.          */
  10.         public class KeyController extends basicController
  11.         {
  12.                 
  13.                 
  14.                 public function KeyController() 
  15.                 {
  16.                         super();
  17.                         setupListener();
  18.                 }
  19.                 /**
  20.                  * 安装侦听器
  21.                  */
  22.                 public function setupListener():void
  23.                 {
  24.                         Global.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
  25.                         Global.stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
  26.                 }
  27.                 /**
  28.                  * 消亡
  29.                  */
  30.                 override public function die():void
  31.                 {
  32.                         Global.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
  33.                         Global.stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp);
  34.                 }
  35.                 /**
  36.                  * 当按键按下时触发
  37.                  * @param        e
  38.                  */
  39.                 protected function onKeyDown(e:KeyboardEvent):void
  40.                 {
  41.                         var me:ActionObject = _target as ActionObject;
  42.                         switch(e.keyCode)
  43.                         {
  44.                                 case 38:
  45.                                         me.direction = ActionObject.UP;
  46.                                         break;
  47.                                 case 40:
  48.                                         me.direction = ActionObject.DOWN;
  49.                                         break;
  50.                                 case 37:
  51.                                         me.direction = ActionObject.LEFT;
  52.                                         break;
  53.                                 case 39:
  54.                                         me.direction = ActionObject.RIGHT;
  55.                                         break;
  56.                                 default:break;
  57.                         }
  58.                 }
  59.                 
  60.                 /**
  61.                  * 当按键弹起时触发
  62.                  * @param        e
  63.                  */
  64.                 protected function onKeyUp(e:KeyboardEvent):void
  65.                 {
  66.                         var me:ActionObject = _target as ActionObject;
  67.                         var active:Array = new Array(37,38,39,40);
  68.                         if (active.indexOf(e.keyCode) != -1) me.direction = 0;
  69.                 }
  70.                 
  71.         }
  72. }
复制代码
这里我们基础到了新的内容.Global.stage.addEventListener,即向舞台(你可以理解为CS中那个白色的工作区,所有动画的根容器)增加侦听器。就是当触发某些事件的时候,会发生通知,我们这里侦听了KeyBoradEvent键盘事件。可以从代码中看到,我们分别增加了2条侦听器,一个是KeyboradEvent.KEY_DOWN,另外一个是KeyboradEvent.KEY_UP。他们的作用分别是,在按键按下的时候(KEY_DOWN)通知或者叫做触发onKeyDown函数,而当按键弹起的时候,触发onKeyUp函数。而在随后,我们使用override覆盖了父级(basicController)的die函数,编写了移除侦听器的动作。一定要记得,增加过什么,就一定要删除掉什么,不然你的游戏占用的内存会越来越大的!

再来看一下键盘事件触发的两个函数。因为我们知道,这个按键控制器所控制的必然是ActionObject,因此,我们可以把控制器的控制目标转换成ActionObject,使用as来进行。之后,就可以更方便的控制目标了。可以看到,我们分别在onKeyDown函数里修改了ActionObject的行走方向,而在onKeyUp函数中恢复行走方向为0.结合前面的代码,当ActionObject的行走方向不为0的时候,它将根据不同的方向做出判断,并移动向不同的方向。

这里的e.KeyCode是键盘事件发送过来的,值即是按下的按键的ASCII码,我们通过它来确定键盘上被按下的是哪几个键,37-40分别是键盘上下左右四个方向键的键值。在onKeyUp中,我们利用数组做了一次判断。如果按下的按键键值在这四个数字组成的数组中,则恢复控制目标的移动方向为0

有了控制器,我们必须有方法把控制器安装到ActionObject中,于是,我们对代码做了如下的修改:
  1. ...
  2.                 /**
  3.                  * 控制器
  4.                  */
  5.                 protected var controller:basicController;
  6.                 
  7.                 /**
  8.                  * 在建立对象的时候,需要传递控制器进来
  9.                  * @param        ctrl
  10.                  */
  11.                 public function ActionObject(ctrl:basicController) 
  12.                 {
  13.                         controller = ctrl;
  14.                         controller.target = this;        // 将控制器的控制目标设置为自己
  15.                         
  16.                 }
  17. ...
  18.                 override public function die():void
  19.                 {
  20.                         controller.die(); //当自己被删除的时候,通知控制器卸载侦听,以释放内存
  21.                 }
  22. ...
复制代码
到这里,控制器部分已经实现了。最后,我们通过游戏舞台把他们组装在一起。

在D5Power下新建Scene目录,并建立一个继承自Sprite类的对象gameScene,来作为游戏场景的基类(如果你喜欢听“亚当”的话,他们是一个意思)。代码如下:
  1. package D5Power.Scene 
  2. {
  3.         import D5Power.Objects.gameObject;
  4.         import flash.display.Sprite;
  5.         import flash.display.Stage;
  6.         import flash.events.Event;
  7.         
  8.         /**
  9.          * 基本游戏场景
  10.          * @author D5.Howard
  11.          */
  12.         public class gameScene extends Sprite
  13.         {
  14.                 /**
  15.                  * 舞台中的对象列表
  16.                  */
  17.                 protected var objectList:Array;
  18.                 /**
  19.                  * 创建游戏基本场景需要传递基本舞台这个参数
  20.                  * @param        _stage        舞台
  21.                  */
  22.                 public function gameScene(_stage:Stage) 
  23.                 {
  24.                         Global.stage = _stage;
  25.                         objectList = new Array();
  26.                         Global.stage.addEventListener(Event.ENTER_FRAME, render);
  27.                 }
  28.                 
  29.                 /**
  30.                  * 向游戏世界中增加新的游戏对象
  31.                  * @param        obj
  32.                  */
  33.                 public function addObject(obj:gameObject):void
  34.                 {
  35.                         if (objectList.indexOf(obj) != -1) return; // 不重复添加
  36.                         objectList.push(obj);
  37.                         addChild(obj);
  38.                 }
  39.                 
  40.                 /**
  41.                  * 从游戏世界中删除游戏对象
  42.                  * @param        obj
  43.                  */
  44.                 public function removeObject(obj:gameObject):void
  45.                 {
  46.                         var id:int = objectList.indexOf(obj);
  47.                         if (id == -1) return;
  48.                         objectList.splice(id,1);
  49.                         removeChild(obj);
  50.                         obj.die();
  51.                 }
  52.                 
  53.                 /**
  54.                  * 渲染函数,通过本函数逐个计算游戏中各对象的动作
  55.                  */
  56.                 public function render(e:Event):void
  57.                 {
  58.                         for each(var obj:gameObject in objectList) obj.Do();
  59.                 }
  60.                 
  61.         }
  62. }
复制代码
游戏舞台就已经构建完成了。我们通过objectList保存目前游戏中的全部游戏对象。并对舞台增加了Event.ENTER_FRAME侦听。这个侦听将在舞台开始播放后,持续不断的运行,来达到动画效果。我们把这个侦听的响应函数设置为render函数,并在这里函数里,循环了场景中全部的游戏对象,并告诉他们,“要做点什么"

最后的工作:画一个方块,并选中它,按F8键将他转换成一个组件,并按下图配置
3.jpg 

我们在这里声明了,这个方框将是一个ActionObject类,转换为组件后,即可把他从舞台上删除掉了,因为我们要用程序来生成他。接下来修改最初的Main.as
  1. package  
  2. {
  3.         import D5Power.Controller.KeyController;
  4.         import D5Power.Objects.ActionObject;
  5.         import D5Power.Scene.gameScene;
  6.         import flash.display.Sprite;
  7.         public class Main extends Sprite
  8.         {
  9.                 
  10.                 public function Main() 
  11.                 {
  12.                         var scene:gameScene = new gameScene(stage);                // 声明游戏舞台
  13.                         var ctrl:KeyController = new KeyController();        // 定义控制器
  14.                         var obj:ActionObject = new ActionObject(ctrl);        // 定义可活动的游戏对象
  15.                         scene.addObject(obj);// 将对象添加到舞台中
  16.                         
  17.                         // 显示游戏舞台
  18.                         addChild(scene);
  19.                 }
  20.                 
  21.         }
  22. }
复制代码
测试影片如下:
 main.swf (1.71 KB) 

源代码如下:
[attach]34715[/attach]

在下一篇的教程里,我们将向场景中增加其他的方框,并让他们随机的在场景中移动。

Teach.rar (11.72 KB)

 

 

最后,来看一下这一篇文章中涉及到的程序的大体结构:
4.jpg 


对于真正的初学者来讲,这样的结构可能稍稍复杂了一些,但是,清晰的结构可能会帮助你的程序易于维护和扩展。欢迎各位感兴趣的朋友进行讨论。有任何问题都可以在论坛中提出,我会进行解答,来帮助大家理解。

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