辣鸡

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

之前我们用flash cs工具做过一个打砖块的游戏,那么现在使用flash Builder可以更加容易地做出具备更多功能,在这个例子中,我们主要要做的是让大家明白三点:一,如何在纯代码条件下绘图;二,充分理解面向对象编程的优势,学会使用良好的MVC(数据-视图-控制三元素分离)思想和良好的代码结构来构建我们的工程。列位道友千万不要把我的教程当做新手基础教材在看,我的教程的目的旨在让列位道友看过《殿堂之路》或一系列基础教材并对AS面向对象编程有所认识的基础上在通过我举出的案例了解这些知识,这些类该如何使用到实际开发中去,并增加开发熟练度。所以今天的教程不会把太多篇幅放在解释一些非常基础的东东,若有不明白的地方还请联系我或者天地会众多弟兄,最好还是自己看看教材多琢磨琢磨吧^_^。
     相信不少兄弟都听说过MVC,这是一种非常好的设计思想,将工程做成数据-视图-控制三元素分离的话将会非常便于修改和维护。我们先从简单的M-V开始,暂时不涉及到C。先为各位介绍一个概念叫做ValueObject,简称VO,它是一个数据结构体,和C语言中的类似,其中只定义了一系列的变量,一般不带有任何逻辑性的函数,项目中的M,即数据层一般都由许许多多个VO组成,它为视图提供数据。比如我们的打砖块游戏里,玩家操作的底板所需的所有数据都可以放到一个VO中,我们给它取个名字叫PadVO,它内部的初步构造为:

  1. package org.moon.vo
  2. {
  3.         public class PadVO
  4.         {
  5.                 public static const EXPAND_WIDTH : int = 60;//前三个常量定义了板处于三种不同长度状态时的长度各为多少,第四个则定义了不会改变的板高度
  6.                 public static const NORMAL_WIDTH : int = 40;
  7.                 public static const SUBSTRACT_WIDTH : int = 20;
  8.                 public static const HEIGHT : int = 10; 
  9.                 
  10.                 public static const STATUS_NORMAL : int = 0;//三个状态各自对应的编号
  11.                 public static const STATUS_EXPAND : int = 1;
  12.                 public static const STATUS_SUBSTRACT : int = 2;
  13.                                 
  14.                                 public var status : int = 0;//板状态,每个状态对应不同长度的板,在吃了道具后会改变
  15.                 public var posX : int;//板的x轴位置
  16.                 public var posY : int;//板的y轴位置
  17.         }
  18. }
复制代码

暂时在游戏中我们就需要这三个数据,所以就定义了这三个,以后若再有数据上的需求,可以很方便地在这里面添加,记得把这些变量都定义为public的让外部能够拿到,在VO中尽量少地定义private的变量,因为VO存在的意义就是为外部视图提供数据支持。一般需要操作数据的视图都需要个VO来与之配对,所以接下来就来看看我们的底板视图类
PadView:

  1. package com.life.view
  2. {
  3.         import flash.display.Sprite;
  4.         
  5.         import com.life.vo.PadVO;
  6.         public class PadView extends Sprite
  7.         {
  8.                 public var padVO : PadVO;
  9.                 
  10.                 public function PadView()
  11.                 {
  12.                         super();
  13.                 }
  14.                 
  15.                 public function updatePos( ) : void {
  16.                         x = padVO.posX;
  17.                         y = padVO.posY;
  18.                 }
  19.                 
  20.                 public function updateGraph( ) : void {
  21.                         graphics.clear( );
  22.                         graphics.beginFill(0);
  23.                         switch(padVO.status) {
  24.                                 case PadVO.STATUS_NORMAL:
  25.                                         graphics.drawRect(-PadVO.NORMAL_WIDTH / 2, -PadVO.HEIGHT / 2, PadVO.NORMAL_WIDTH, PadVO.HEIGHT);
  26.                                         break;
  27.                                 case PadVO.STATUS_EXPAND:
  28.                                         graphics.drawRect(-PadVO.EXPAND_WIDTH / 2, -PadVO.HEIGHT / 2, PadVO.EXPAND_WIDTH, PadVO.HEIGHT);
  29.                                         break;
  30.                                 case PadVO.STATUS_SUBSTRACT:
  31.                                         graphics.drawRect(-PadVO.SUBSTRACT_WIDTH / 2, -PadVO.HEIGHT / 2, PadVO.SUBSTRACT_WIDTH, PadVO.HEIGHT);
  32.                                         break;
  33.                         }
  34.                         
  35.                         graphics.endFill( );
  36.                 }
  37.                 
  38.         }
  39. }
复制代码

这样的类中应该没有什么难懂的代码,我们看到它内部含有一个与之对应的PadVO实例来提供数据支持。唯一需要注意的地方就是在updateGraph()函数中对不同类型的板要绘制不同的长度,这里必须把绘制Rect的左上角坐标定义为负的长度和宽度,因为我们得保证板的中心点位置必须保持在视图的中心位置才行。有的道友可能会问,我那些sprite的视图类中都有x,y变量来表示这个类的位置了,干嘛还在它对应的VO中定义个什么xPos,yPos呢,岂不是多此一举?我的回答是,若是多此一举我还写这些干嘛,我还怎么在列位仙家中混……直接使用视图的x,y来进行数据运算是一个非常具局限性的办法,因为你一旦改变了它们,视图就马上发生了位移,可能造成bug,比如你想多次改变PadView的横坐标,若直接操作它的x值将会看见底板自动乱跑,若是使用其内部的数据对象VO来进行运算,不论做多少次运算,在你调用updatePos()方法之前视图的位置都不会发生改变。
         暂时先说这些吧,今天这个案例和以往比较起来算是比较大了,所以需要我多次续写才行……

继续我们上次没说完的话题吧,上回我们写到我们打砖块游戏所需的底板它的视图以及和它对应的VO,有了这两个东西我们的底板就算构建完成了,在PadView 类中,我们定义了两个public的方法updatePos()和updateGraph(),前者用来更新视图的位置,后者用来更新视图的外观,有了这两个方法我们就差不多可以在外部灵活地操作一个底板了。接下来我们需要一起来构建我们的小球类,依照和创建底板类一样的创建流程,我们就可以轻松地写出以下两个文件:
BallVO:

  1. package com.life.vo
  2. {
  3.         public class BallVO
  4.         {
  5.                 public var posX : int;
  6.                 public var posY : int;
  7.                 
  8.                 public var speedX : Number;
  9.                 public var speedY : Number;
  10.         }
  11. }
复制代码

BallView:

  1. package com.life.view
  2. {
  3.         import flash.display.Sprite;
  4.         import com.life.vo.BallVO;
  5.         
  6.         public class BallView extends Sprite
  7.         {
  8.                 public var ballVO : BallVO;
  9.                 
  10.                 public function BallView()
  11.                 {
  12.                         init( );
  13.                 }
  14.                 
  15.                 public function updatePos( ) : void {
  16.                         x = ballVO.posX;
  17.                         y = ballVO.posY;
  18.                 }
  19.                 
  20.                 private function init( ) : void {
  21.                         graphics.beginFill(0);
  22.                         graphics.drawCircle(0,0,10);
  23.                         graphics.endFill();                
  24.                 }
  25.         }
  26. }
复制代码

是不是一眼就能看懂了?它们唯一不同之处就在于小球不需要改变视图,因为它不像底板一样能够根据吃到的道具变长或者变短,所以我们只需要在初始化的时候就给它画好外形即可。
     接下来轮到今天的主角“砖块”出场了,考虑到砖块有多种形态:有的被打掉后会掉落物品,掉落的物品又分两种(你也可以分多种),一种物品吃了可以增加你的长度(自从吃了这个物品,老婆说你越来越棒了),一种吃了会使你变短(遥想当年,哥也是驰骋过夜总会的男人……);有的砖块是如此地强壮,以至于你一击无法将其击破……那么我们应该如何做呢?
     想必大家之前在看这篇帖子前一定学过了“继承”的概念,但只是知道有这么个概念并不知道怎么用,今天我们就来用一下它,看看继承的好处在哪里。在动手写代码前我们需要想一想所有这些不同种类的砖块的共同之处有哪些地方,一,是他们内部都必须有一个和之对应的VO来提供数据;二,他们都需要有方法来让外部调用以更新其外观及位置。好了,暂时我就想到这两点,以后若有需要再扩充吧……OK,let`s fucking go!
BrickVO:

  1. package com.life.vo
  2. {
  3.         public class BrickVO
  4.         {
  5.                 public static const NORMAL : int = 0;
  6.                 public static const HARD : int = 1;
  7.                 public static const SUPER_HARD : int = 2;
  8.                 public static const EXPAND : int = 3;
  9.                 public static const SUBSTRACT : int = 4;
  10.                 
  11.                 public static const WIDTH : int = 30;
  12.                 public static const HEIGHT : int = 10;
  13.                 
  14.                 public var hitCount : int = 0;        //撞击计数                
  15.                 public var type : int;
  16.                 
  17.                 public var posX : int;
  18.                 public var posY : int;
  19.         }
  20. }
复制代码

BaseBrickView:

  1. package com.life.view
  2. {
  3.         import flash.display.Sprite;
  4.         import com.life.vo.BrickVO;
  5.         public class BaseBrickView extends Sprite
  6.         {
  7.                 public var brickVO : BrickVO
  8.                 
  9.                 public function BaseBrickView()
  10.                 {
  11.                         super();
  12.                 }
  13.                 
  14.                 public function updatePos( ) : void {
  15.                         x = brickVO.posX;
  16.                         y = brickVO.posY;
  17.                 }
  18.                 
  19.                 public function updateGraph( ) : void {
  20.                         graphics.clear( );
  21.                         graphics.beginFill(0);
  22.                         graphics.drawRect(0,0,BrickVO.WIDTH,BrickVO.HEIGHT);
  23.                         graphics.endFill( );
  24.                 }
  25.         }
  26. }
复制代码

需要提醒的是,我们不要忽略常量(即以static const定义的东东)的作用,在我之前列出的视图类中在绘画时使用的长和宽都使用了常量来代替单纯的数字,其好处有两点:
1、降低写错的可能性:我们知道,在FB中有着强大的代码提示功能,按Alt + / 快捷键就能弹出代码提示框,利用这一点,我们定义常量来代替基础数据类型的话可以在编码时获得代码提示,防止我们在写数字时漏掉一个0或者什么,在写字符串时写错字母位置等等。
2、便于修改:当我们在所有需要一个数据(比如我们这里的砖块长、宽)的地方都使用一个常量来代替基础数据类型时,当我们需要修改数据时只需要修改这个常量的值即可在工程中所有用到之的地方一起改变,不需要手动去一个个文件地找了,费时不说还很容易漏掉。(如果我们没把砖块长度定义为一个常量BrickVO.WIDTH,在所有绘图的地方都用30来代替,那么当我们需要修改砖块长度时就必须到一个个文件去找这个“30”并改之,当工程大的时候很麻烦还容易漏掉几处)。

有了老爹,接下来就改创建他他的子孙了。先创建“奖励砖块”和“惩罚砖块”:
ExpandBrick:

  1. package com.life.view.brick
  2. {
  3.         import com.life.view.BaseBrickView;
  4.         import com.life.vo.BrickVO;
  5.         public class ExpandBrick extends BaseBrickView
  6.         {
  7.                 public function ExpandBrick()
  8.                 {
  9.                         super();
  10.                 }
  11.                 
  12.                 override public function updateGraph( ):void {
  13.                         graphics.clear();
  14.                         graphics.beginFill(0x000ff0);
  15.                         graphics.drawRect(0,0,5, BrickVO.HEIGHT);//至于这里一连串语句到底画出来是个什么鬼东西,运行一下看结果就晓得了
  16.                         graphics.drawRect(6,0,5, BrickVO.HEIGHT);
  17.                         graphics.drawRect(12,0,5, BrickVO.HEIGHT);
  18.                         graphics.drawRect(18,0,5, BrickVO.HEIGHT);
  19.                         graphics.drawRect(24,0,5, BrickVO.HEIGHT);
  20.                         graphics.endFill( );
  21.                 }
  22.         }
  23. }
复制代码

SubstractBrick:

  1. package com.life.view.brick
  2. {
  3.         import com.life.view.BaseBrickView;
  4.         import com.life.vo.BrickVO;
  5.         public class SubstractBrick extends BaseBrickView
  6.         {
  7.                 public function SubstractBrick()
  8.                 {
  9.                         super();
  10.                 }
  11.                 
  12.                 override public function updateGraph( ):void {
  13.                         graphics.clear();
  14.                         graphics.beginFill(0x0ff000);
  15.                         graphics.drawCircle(3,BrickVO.HEIGHT / 2,3);
  16.                         graphics.drawCircle(9,BrickVO.HEIGHT / 2,3);
  17.                         graphics.drawCircle(15,BrickVO.HEIGHT / 2,3);
  18.                         graphics.drawCircle(21,BrickVO.HEIGHT / 2,3);
  19.                         graphics.drawCircle(27,BrickVO.HEIGHT / 2,3);
  20.                         graphics.endFill( );
  21.                 }
  22.                 
  23.         }
  24. }
复制代码

它们与它们的老子唯一不同的地方也就是长得不一样罢了。再来看“加强型砖块”与“超强型砖块”:
HardBrick:

  1. package com.life.view.brick
  2. {
  3.         import com.life.view.BaseBrickView;
  4.         import com.life.vo.BrickVO;
  5.         public class HardBrick extends BaseBrickView
  6.         {
  7.                 public function HardBrick()
  8.                 {
  9.                         super();
  10.                 }
  11.                 
  12.                 override public function updateGraph( ):void {
  13.                         graphics.clear();
  14.                         switch(brickVO.hitCount){
  15.                                 case 0:
  16.                                         graphics.beginFill(0x0000ff);
  17.                                         break;
  18.                                 case 1:
  19.                                         graphics.beginFill(0x000000);
  20.                                         break;
  21.                         }
  22.                         graphics.drawRect(0,0,BrickVO.WIDTH, BrickVO.HEIGHT);
  23.                 }
  24.         }
  25. }
复制代码

SuperHardBrick:

  1. package com.life.view.brick
  2. {
  3.         import com.life.view.BaseBrickView;
  4.         import com.life.vo.BrickVO;
  5.         public class SuperHardBrick extends BaseBrickView
  6.         {
  7.                 public function SuperHardBrick()
  8.                 {
  9.                         super();
  10.                 }
  11.                 
  12.                 
  13.                 override public function updateGraph( ):void {
  14.                         graphics.clear();
  15.                         switch(brickVO.hitCount){
  16.                                 case 0:
  17.                                         graphics.beginFill(0x00ff00);
  18.                                         break;
  19.                                 case 1:
  20.                                         graphics.beginFill(0x0000ff);
  21.                                         break;
  22.                                 case 2:
  23.                                         graphics.beginFill(0x000000);
  24.                                         break;
  25.                         }
  26.                         graphics.drawRect(0,0,BrickVO.WIDTH, BrickVO.HEIGHT);
  27.                 }
  28.         }
  29. }
复制代码

我们看到这两位耐打型选手的updateGraph()方法中有点不一样的玩意儿,它会在每次被调用时根据其VO中记录的被撞击次数来更新其外形,挨打前后的颜色不一样。
若你想在子类中绘制和父类一样的图形,即执行和父类updateGraph()方法中一模一样的代码,那么在加上了override重载关键字的updateGraph方法中可以通过使用super.updateGraph()来做到, 也可以直接不用写这个新的updateGraph(),因为他默认已继承了父类的此方法。在这里我们需要画和父类不一样的外表,所以在所有砖块子类中都需要使用override关键字来重载updateGraph方法。

最后,别忘了我们还有一样东东要添加,那就是我们的道具类,依然是M-V的结构:
BonusVO:

  1. package com.life.vo
  2. {
  3.         public class BonusVO
  4.         {
  5.                 public static const EXPAND : int = 0;
  6.                 public static const SUBSTRACT : int = 1;
  7.                 
  8.                 public var dropSpeed : int = 5;
  9.                 public var type : int;
  10.                 public var posX : int;
  11.                 public var posY : int;
  12.         }
  13. }
复制代码

BonusView:

  1. package com.life.view
  2. {
  3.         import flash.display.Sprite;
  4.         
  5.         import com.life.vo.BonusVO;
  6.         public class BonusView extends Sprite
  7.         {
  8.                 public var bonusVO : BonusVO;
  9.                 
  10.                 public function BonusView()
  11.                 {
  12.                         super();
  13.                 }
  14.                 
  15.                 public function updatePos( ) : void {
  16.                         x = bonusVO.posX;
  17.                         y = bonusVO.posY;
  18.                 }
  19.                 
  20.                 public function updateGraphic( ) : void {                        
  21.                         switch(bonusVO.type){
  22.                                 case BonusVO.EXPAND:
  23.                                         graphics.beginFill(0x00ffff)
  24.                                         graphics.drawEllipse(0,0,10,4);                
  25.                                         break;
  26.                                 case BonusVO.SUBSTRACT:
  27.                                         graphics.beginFill(0xffff00);
  28.                                         graphics.drawEllipse(0,0,4,10);
  29.                                         break;
  30.                         }
  31.                         graphics.endFill( );
  32.                 }
  33.         }
  34. }
复制代码

道具分为两种,一种是增大增粗的ZYY一种是变小变细的萎哥,在VO里分别用常量定义之并在VIEW里根据他们类型不同绘制不同外形。
     好了,最后我们要做的就只剩把这些零碎的元素组合起来了,带来我们今天的终极BOSS:
GameLayer:

  1. package com.life.view
  2. {
  3.         import flash.display.Sprite;
  4.         import flash.events.Event;
  5.         
  6.         import com.life.view.brick.ExpandBrick;
  7.         import com.life.view.brick.HardBrick;
  8.         import com.life.view.brick.SubstractBrick;
  9.         import com.life.view.brick.SuperHardBrick;
  10.         import com.life.vo.BallVO;
  11.         import com.life.vo.BonusVO;
  12.         import com.life.vo.BrickVO;
  13.         import com.life.vo.PadVO;
  14.         public class GameLayer extends Sprite
  15.         {
  16.                 private const RESTRICT_TOP : int = 0;
  17.                 private const RESTRICT_LEFT : int = 0;
  18.                 private const RESTRICT_RIGHT : int = 400;
  19.                 private const RESTRICT_BOTTOM : int = 400;
  20.                 
  21.                 private var ballView : BallView;
  22.                 private var padView : PadView;
  23.                 private var brickViewList : Array = new Array( );
  24.                 private var dropItemViewList : Array = new Array( );
  25.                 
  26.                 public function GameLayer()
  27.                 {
  28.                         super( );
  29.                         init( );
  30.                 } 
  31.                 
  32.                 private function init( ) : void {
  33.                         initBallView( );
  34.                         initBricksView( );
  35.                         initPadView( );
  36.                         initListener( );
  37.                 }
  38.                 
  39.                 private function initBallView( ) : void {
  40.                         ballView = new BallView( );
  41.                         var tmpBallVO : BallVO = new BallVO( );
  42.                         tmpBallVO.posX = 100;
  43.                         tmpBallVO.posY = 300;
  44.                         tmpBallVO.speedY = -5;
  45.                         tmpBallVO.speedX = 2;
  46.                         ballView.ballVO = tmpBallVO;
  47.                         ballView.updatePos( );
  48.                         addChild(ballView);
  49.                 }
  50.                 
  51.                 private function initBricksView( ) : void {                        
  52.                         for(var i : int = 0; i < 50; i++) {
  53.                                 var tmpVO : BrickVO = new BrickVO( );
  54.                                 tmpVO.posX = 50 + i % 10 * 40;
  55.                                 tmpVO.posY = 50 + int(i / 10) * 30;
  56.                                 tmpVO.type = Math.floor(5 * Math.random());
  57.                                 var tmpView : BaseBrickView = getBrick(tmpVO.type);
  58.                                 tmpView.brickVO = tmpVO;                                
  59.                                 tmpView.updatePos( );
  60.                                 tmpView.updateGraph( );
  61.                                 brickViewList.push(tmpView);
  62.                                 addChild(tmpView);
  63.                         }                        
  64.                 }
  65.                 
  66.                 private function initPadView( ) : void {
  67.                         padView = new PadView( );
  68.                         var tmpPadVO : PadVO = new PadVO( );
  69.                         tmpPadVO.posX = 200;
  70.                         tmpPadVO.posY = 300;
  71.                         padView.padVO = tmpPadVO;
  72.                         padView.updatePos( );
  73.                         padView.updateGraph( );
  74.                         addChild(padView);        
  75.                 }
  76.                 
  77.                 private function initListener( ) : void {
  78.                         addEventListener(Event.ENTER_FRAME, efHandler);
  79.                 }
  80.                 
  81.                 private function efHandler(event : Event) : void {
  82.                         movePad( );                
  83.                         moveBall( );
  84.                         moveBonus( );
  85.                         checkBallHit( );
  86.                 } 
  87.                 
  88.                 private function movePad( ) : void {
  89.                         padView.padVO.posX = mouseX;
  90.                         padView.updatePos( );        
  91.                 }
  92.                 
  93.                 private function moveBall( ) : void {
  94.                         var tmpBallVO : BallVO = ballView.ballVO;                        
  95.                         tmpBallVO.posX += tmpBallVO.speedX;
  96.                         tmpBallVO.posY += tmpBallVO.speedY;
  97.                         ballView.updatePos( );
  98.                 }
  99.                 
  100.                 private function moveBonus( ) : void {
  101.                         for each(var elem : BonusView in dropItemViewList) {
  102.                                 elem.bonusVO.posY += elem.bonusVO.dropSpeed; 
  103.                                 elem.updatePos( );
  104.                         }
  105.                 }
  106.                 
  107.                 private function checkBallHit( ) : void {
  108.                         //检测砖块碰撞
  109.                         checkBrickHit( );
  110.                         //检测小板碰撞
  111.                         checkPadHit( );
  112.                         //检测边界碰撞
  113.                         checkEdgeHit(ballView.ballVO);
  114.                         //检测是否吃到奖励
  115.                         checkBonusHit( );
  116.                 }
  117.                 
  118.                 private function checkBrickHit( ) : void {
  119.                         for(var i : int = 0; i < brickViewList.length; i++) {
  120.                                 if(brickViewList[i].hitTestObject(ballView)){
  121.                                         //改变速度
  122.                                         ballView.ballVO.speedY *= -1;
  123.                                         (brickViewList[i] as BaseBrickView).brickVO.hitCount++;
  124.                                         //移除砖块
  125.                                         if(checkBrickToRemove(brickViewList[i].brickVO)){
  126.                                                 
  127.                                                 dropBonusItem(brickViewList[i].brickVO);
  128.                                                 //移除                
  129.                                                 removeChild(brickViewList[i]);
  130.                                                 brickViewList.splice(i,1);
  131.                                                 i--;                                                
  132.                                         }else{
  133.                                                 //不移除则更新样式
  134.                                                 (brickViewList[i] as BaseBrickView).updateGraph( );
  135.                                         }        
  136.                                 }
  137.                         }
  138.                 }
  139.                 
  140.                 private function checkPadHit( ) : void {
  141.                         if(ballView.hitTestObject(padView)){
  142.                                 ballView.ballVO.speedY = -ballView.ballVO.speedY;
  143.                                 //改变水平坐标的碰撞
  144.                                 ballView.ballVO.speedX = (ballView.ballVO.posX - padView.padVO.posX) / 5;
  145.                         }
  146.                 }
  147.                 
  148.                 private function checkEdgeHit(tmpBallVO : BallVO) : void {
  149.                         if(tmpBallVO.posX > RESTRICT_RIGHT || tmpBallVO.posX < RESTRICT_LEFT){
  150.                                 tmpBallVO.speedX = -tmpBallVO.speedX;
  151.                         }
  152.                         if(tmpBallVO.posY > RESTRICT_BOTTOM || tmpBallVO.posY < RESTRICT_TOP) {
  153.                                 tmpBallVO.speedY = -tmpBallVO.speedY;
  154.                         }
  155.                 }
  156.                 
  157.                 private function checkBonusHit( ) : void {
  158.                         for(var i : int = 0; i < dropItemViewList.length; i++) {
  159.                                 if(dropItemViewList[i].hitTestObject(padView)){
  160.                                         //移除砖块
  161.                                         //触发碰撞特效
  162.                                         doHitEffect(dropItemViewList[i].bonusVO.type);                
  163.                                         //移除                
  164.                                         removeChild(dropItemViewList[i]);
  165.                                         dropItemViewList.splice(i,1);
  166.                                                                                 i--;
  167.                                 }
  168.                         }
  169.                 }
  170.                 
  171.                 private function dropBonusItem(brickVO : BrickVO) : void {
  172.                         var tmpBonusView : BonusView;
  173.                         var tmpBonusVO : BonusVO  = new BonusVO( );
  174.                         switch(brickVO.type){
  175.                                 case BrickVO.EXPAND: tmpBonusVO.type = BonusVO.EXPAND; break;
  176.                                 case BrickVO.SUBSTRACT: tmpBonusVO.type = BonusVO.SUBSTRACT; break;
  177.                         }
  178.                         switch(brickVO.type){
  179.                                 case BrickVO.EXPAND:
  180.                                 case BrickVO.SUBSTRACT:
  181.                                         tmpBonusVO.posX = brickVO.posX;
  182.                                         tmpBonusVO.posY = brickVO.posY;
  183.                                         tmpBonusView = new BonusView( );        
  184.                                         tmpBonusView.bonusVO = tmpBonusVO;
  185.                                         tmpBonusView.updateGraphic();
  186.                                         dropItemViewList.push(tmpBonusView);
  187.                                         addChild(tmpBonusView);
  188.                                                                                 break;        
  189.                                 default:                                        
  190.                                         break;
  191.                         }
  192.                 }
  193.                 
  194.                 private function doHitEffect(type : int) : void {
  195.                         switch(type){
  196.                                 case BonusVO.EXPAND:
  197.                                         padView.padVO.status = PadVO.STATUS_EXPAND;
  198.                                         padView.updateGraph( );
  199.                                         break;
  200.                                 case BonusVO.SUBSTRACT:
  201.                                         padView.padVO.status = PadVO.STATUS_SUBSTRACT;
  202.                                         padView.updateGraph( );
  203.                                         break;
  204.                         }
  205.                 }
  206.                 
  207.                 private function checkBrickToRemove(brickVO : BrickVO) : Boolean {
  208.                         switch(brickVO.type){
  209.                                 case BrickVO.NORMAL:
  210.                                 case BrickVO.SUBSTRACT:
  211.                                 case BrickVO.EXPAND:
  212.                                         if(brickVO.hitCount >= 1) return true;
  213.                                         break;
  214.                                 case BrickVO.HARD:        
  215.                                         if(brickVO.hitCount >= 2) return true;
  216.                                         break;
  217.                                 case BrickVO.SUPER_HARD:        
  218.                                         if(brickVO.hitCount >= 3) return true;
  219.                                         break;
  220.                         }
  221.                         return false;
  222.                 }
  223.                 
  224.                 /**
  225.                  * 按砖块类型返还相应实例
  226.                  */ 
  227.                 private function getBrick(type : int) : BaseBrickView {
  228.                         switch(type){
  229.                                 case BrickVO.EXPAND:
  230.                                         return new ExpandBrick( );
  231.                                         break;
  232.                                 case BrickVO.HARD:
  233.                                         return new HardBrick( );
  234.                                         break;
  235.                                 case BrickVO.SUBSTRACT:
  236.                                         return new SubstractBrick( );
  237.                                         break;
  238.                                 case BrickVO.SUPER_HARD:
  239.                                         return new SuperHardBrick( );
  240.                                         break;
  241.                                 default:
  242.                                         return new BaseBrickView( );
  243.                                         break
  244.                         }
  245.                 }
  246.                         
  247.         }
  248. }
复制代码
 
是不是一下看了那么多代码有点蛋疼?没事,我跟列位道友一起来分析一下它一步一步到底做了些神马。
    在init函数中初始化了一我们游戏所需的三要素:球、底板以及砖块并添加了事件侦听器,初始化时遵循先数据再视图的原则,在生成砖块时还需要随机出每块砖的类型,别忘了在那些没有设置过位置或外观的view中需调用updatePos()或者updateGraph()来设置一下。再看到我们的重点——efHandler函数。先看movePad(),它的代码没什么难度,就是把板的位置和鼠标位置绑定住并调用updatePos来更新视图的坐标,记住哦,在调用updatePos之前必须得先改变其中VO的响应数据xPos,yPos才行。继续看moveBall(),也没什么难度,在ballView内部VO已设置过速度的情况下只要把位置加上速度即可。在每产生一个道具时都会被第一时间添加到一个叫做dropItemViewList的数组中收集起来,moveBonus()中做的事情就是把dropItemViewList中所有道具同步移动,让它们一起下落,若dropItemViewList中没有元素,则什么也不会发生。最后看看checkBallHit(),这才是主要难点所在哦,它由四个功能函数联手工作。
     先看checkBrickHit()函数吧,它的职责是检测球是否撞到了砖块,并处理撞到后的逻辑。至于如何判断球与砖块的碰撞,相信看过我教程三的道友们已经闭着眼睛都能写出来了,关键是碰撞到之后的逻辑该如何处理。第一步无疑是反弹小球,第二步我们看到这句代码:
  1. (brickViewList[i] as BaseBrickView).brickVO.hitCount++;
复制代码
不论你砖块是什么类型,是HardBrick类型也好、SubstractBrick等类型也罢,它们都有一个共同的老爸——BaseBrickView,所以不论砖块是什么类型,都可以把他们向上转型至父类然后调用统一的变量或者方法,就是说你不必管当前操作的砖块是什么类型,你只要知道这是一块砖头就行了,而砖头视图中必然存在和它对应的brickVO,这是所有砖块类都从父类中继承过来了的,因此,上面这句代码就充分体现出了面向对象中“继承”这一概念的其中一个优点“向上转型”,它使得所有继承自同一父类的子类可以放在一起集中处理数据。可能初涉此知识的道友暂时不能体会,没关系,随着经验的积累慢慢就会懂了。这句话就让我们所有的砖块被碰撞次数加一。
     接下来让我们看下面这个条件语句:
  1. if(checkBrickToRemove(brickViewList[i].brickVO)){                                           
  2.                                                 dropBonusItem(brickViewList[i].brickVO);
  3.                                                 //移除                
  4.                                removeChild(brickViewList[i]);
  5.                                                 brickViewList.splice(i,1);
  6.                                                 i--;                                                
  7.                                         }else{
  8.                                                 //不移除则更新样式
  9.                                 (brickViewList[i] as BaseBrickView).updateGraph( );
  10.                                         }
复制代码
它先把当前检测到被球撞到的砖块的VO:brickViewList[ i ].brickVO作为参数传给我们检测是否需要移除砖块的审判官checkBrickToRemove()方法,该方法会根据你传入的VO,根据其砖块类别以及VO中记录的被碰撞次数判断是否其达到了移除的标准,对于普通砖块、奖励砖块还有惩罚砖块来说他们只要碰撞次数达到1就得被移除掉了,而其它类型的砖块可能要碰撞次数达到2次或更多才行。在我们的审判官checkBrickToRemove()方法做出决定后,若返回值为true,那么好,你这个砖块就得挂了,if的条件成立,进入死刑执行阶段,先把该View的VO即brickViewList[ i ].brickVO传入检测是否会掉落物品函数dropBonusItem()中,若参数VO的类型type为奖励类型BrickVO.EXPAND或者惩罚类型BrickVO.SUBSTRACT的话就生成一个道具视图(当然还有随之配套的VO)并添加到dropItemViewList中去,这样就会在每一帧到来时它都会下落了(因为efHandler中moveBonus()起作用了),记得道具产生的位置要和产生它的砖块一致哦。之后调用removeChild将死刑犯执行枪决让他消失,还需要除去其在brickViewList中的记录,防止在brickViewList被遍历时把这个死人的数据也一并改了,由于调用了splice()从数组中除去了元素,所以数组的长度发生了改变,比如死刑犯原来所在位置为2,那么此时 i 就等于2,在其被从数组中排除后,2的位置将由原先的3号位置的人代替,而3号位置又将会被4号位置的人前赴后继,但是在下一次循环时 i 将会自加(i++)变为3,那么下一次循环时系统将会去检查3号位置的人,此时待在3号位置的是原来站在4号位置的人,那我们原来站在3号位置的这位老兄就会被系统检测给遗漏掉了。为了防止此类事件的发生,在调用splice()改变数组结构后需加上一句 i-- 。至于else中的语句,再次出现了向上转型的情况,这里大家想必都能看懂了。
     接下来再看checkPadHit(),这里有疑问的一句就是
  1. //改变水平坐标的碰撞
  2. ballView.ballVO.speedX = (ballView.ballVO.posX - padView.padVO.posX) / 5;
复制代码
这个算法和教程3中的不一样吧?因为这是高级打砖块游戏,总得来点更加生动的效果,在简单的打砖块游戏中我们可以直接在球碰到底板时把它的y速度取反以达到反弹的效果,但若考虑再仔细一点就会发现更贴近常识的现象应该是:球从左边飞来,碰到板左半部分后应该会往左弹回而不是继续往它的x方向行进,从右边飞来碰到板右半部分也是一样的,所以我就想到一个办法让球的横坐标减去板的横坐标,若球是从左边飞来的那么这个算式的结果就会小于0,再把这个小于0的结果赋值给球速度,那球就会往左飞回去啦,当然,还需要在这个结果后除以一定系数,避免得出的速度太大造成不现实的视觉效果。
<ignore_js_op>1.jpg 
         接下来的边界检测不用说各位也懂的,若你想让球触碰舞台底部让游戏结束,就改变一下这里面的代码好了。最后的这个checkBonusHit()函数中也没什么难点,若各位道友有疑问请跟贴发问,我一般都会解答的。
      写完最后的这个GameLayer.as文件后可直接把该文件设为默认应用程序启动,也可以集成到一个总应用中,像我这样:
MainGame:
  1. package {
  2.         import flash.display.Sprite;
  3.         import com.life.view.GameLayer;
  4.         [SWF(width="500", height="500", frameRate="30")] 
  5.         public class MainGame extends Sprite        
  6.         {
  7.                 private var gameLayer : GameLayer;
  8.                 
  9.                 public function MainGame()
  10.                 {
  11.                         init( );
  12.                 }
  13.                 
  14.                 private function init( ) : void {
  15.                         initGameLayer( );        
  16.                 }
  17.                 
  18.                 private function initGameLayer( ) : void {
  19.                         gameLayer = new GameLayer( );
  20.                         addChild(gameLayer);
  21.                 }
  22.         }
  23. }
复制代码
在这个主应用文件中就显得是那么地简约了,主应用就像一个大厅,把所有家具整合起来,它不用关心家具内部是什么构造,作为主人的你可以随自己喜欢往客厅里放家具。这里只放了GameLayer这个游戏主界面类,你大可以再创造一个开始界面一个结束界面放入主应用文件中,当点击开始按钮后用removeChild移去开始界面并addChild游戏界面,当游戏进行中小球碰底或者打完了砖块就弹出GAME OVER界面。
最后看看结果是不是和我们想象中的那样:http://www.iamsevent.com/upload/AdvanceBrickGame.swf
附上大家翘首以盼的源码,记得把它们放在src目录下哦。
<ignore_js_op> AdvanceBrickGame.rar (6.41 KB, 下载次数: 663)

 

posted on 2012-10-05 16:42  辣鸡  阅读(464)  评论(0编辑  收藏  举报