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


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

  • 相关阅读:
    C++范围解析运算符::的使用
    C程序的内存布局
    ARM中LDR伪指令与LDR加载指令
    每天一个linux命令(12):more命令
    C++ explicit关键字
    C++内联函数详解
    C++友元详解
    C++ new操作符详解
    CDN技术详解笔记
    字符串匹配(KMP 算法 含代码)
  • 原文地址:https://www.cnblogs.com/keng333/p/2304949.html
Copyright © 2011-2022 走看看