`
mar88mar
  • 浏览: 37769 次
社区版块
存档分类
最新评论

使用Flex和Actionscript开发Flash游戏――碰撞检测

 
阅读更多

  这一部分,我们加入碰撞检测,让玩家能够真正的攻击敌机。
  顾名思义,碰撞检测就是能够探测两个物体碰撞,并且做出相应的反应。第五部分里,我们的飞船已
  经能够将子弹射向敌机。唯一的问题就是子弹穿过了敌机。这部分中,我们要用代码实现碰撞检测,将敌
  机击落。
  表面上看碰撞检测很简单,但是实际上,这是一个非常难实现的概念。你能发现有的书整本都在讲述
  二维物体间及三维物体间的相交性。幸好,我们使用的碰撞检测比较简单。每一个画面上的物体都拥有一
  个矩形包围盒,用来进行碰撞检测。这些矩形包围盒与下面图像的长宽一致。当矩形重叠时,我们就认为
  是碰撞了。
  最好是将图片裁剪到没有空白边,以便更加精确的进行碰撞检测。例如:上图就比下图更优。下图
  的空白部分同样会被用于碰撞检测。
  [img[/img]
  接下来,让我们看一下GameObject类的改变:
  GameObject.as package
  {
  import flash.display.*;
  import flash.events.*;
  import flash.geom.*;
  /*
  The base class for all objects in the game.
  */
  public class GameObject
  {
  // object position
  public var position:Point = new Point(0, 0);
  // higher zOrder objects are rendered on top of lower ones
  public var zOrder:int = 0;
  // the bitmap data to display 
  public var graphics:GraphicsResource = null;
  // true if the object is active in the game
  public var inuse:Boolean = false;
  public var collisionArea:Rectangle;
  public var collisionName:String = CollisionIdentifiers.NONE; 
  public function get CollisionArea():Rectangle
  {
  return new Rectangle(position.x, position.y, collisionArea.width, collisionArea.height);
  } 
  public function GameObject()
  {
  }
  public function startupGameObject(graphics:GraphicsResource, position:Point, z:int = 0):void
  {
  if (!inuse)
  {
  this.graphics = graphics;
  this.zOrder = z;
  this.position = position.clone();
  this.inuse = true;
  GameObjectManager.Instance.addGameObject(this);
  setupCollision();
  }
  }
  public function shutdown():void
  {
  if (inuse)
  { 
  graphics = null;
  inuse = false;
  GameObjectManager.Instance.removeGameObject(this);
  }
  }
  public function copyToBackBuffer(db:BitmapData):void
  {
  db.copyPixels(graphics.bitmap, graphics.bitmap.rect, position, graphics.bitmapAlpha, new 
  Point(0, 0), true);
  }
  public function enterFrame(dt:Number):void
  {
  }
  public function click(event:MouseEvent):void
  {
  }
  public function mouseDown(event:MouseEvent):void
  {
  }
  public function mouseUp(event:MouseEvent):void
  {
  }
  public function mouseMove(event:MouseEvent):void
  {
  }
  protected function setupCollision():void
  {
  collisionArea = graphics.bitmap.rect;
  }
  public function collision(other:GameObject):void
  {
  }
  }
  }
  Read more: http://www.brighthub.com/internet/web-development/ articles/11889.aspx?
  p=2#ixzz0Q3kP0KCc
  复制代码我们添加了两个属性:collisionArea和collisionName。collisionArea表示之前我们说过的矩
  形。collisionName定义了物体的类型。例如:武器拥有名字"PlayerWeapon",敌人拥有名字"Enemy"
  。默认名字为"None",用CollisionIdentifiers.NONE来指定。
  我们还添加了3个方法:collision, CollisionArea和setupCollision。collision方法是另一个空方
  法,需要子类去覆写。当碰撞检测到时,它将被GameObjectManager调用。setupCollision方法用于保存
  用于碰撞检测系统的图像大小。CollisionArea返回矩形包围盒当前的屏幕位置。(未完待续) 
  你或许会奇怪,既然包围盒与图像尺寸一样,为什么还要单独使用一个collisionArea属性呢?原因是在
  第7部分我们要将动画加入游戏。动画类将重写setupCollision方法。
  CollisionIdentifiers.aspackage
  {
  public class CollisionIdentifiers
  {
  public static const NONE:String = "None";
  public static const PLAYER:String = "Player";
  public static const PLAYERWEAPON:String = "PlayerWeapon";
  public static const ENEMYWEAPON:String = "EnemyWeapon";
  public static const ENEMY:String = "Enemy";
  public static const POWERUP:String = "Powerup";
  }
  }
  Read more: http://lp2.fanqiang.org/browse.php?
  u=Oi8vd3d3LmJyaWdodGh1Yi5jb20vaW50ZXJuZXQvd2ViLWRl dmVsb3BtZW50L2FydGljbGVzLzExODg5LmFzcHg%
  2FcD0y&b=5#ixzz0Q6NoySPG
  复制代码和ZOrders类一样,CollisionIdentifiers类用于保存一些预先设定的静态值。为了防止名字撞
  车。CollisionIdentifiers.PLAYER进行自我解释,就是说"Player"字符串并不代表其本身的意思。
  接下来看一下GameObjectManager类。
  GameObjectManager.aspackage
  {
  import flash.display.*;
  import flash.events.*;
  import flash.utils.*;
  import mx.collections.*;
  import mx.core.*;
  public class GameObjectManager
  {
  // double buffer
  public var backBuffer:BitmapData;
  // colour to use to clear backbuffer with
  public var clearColor:uint = 0xFF0043AB;
  // static instance
  protected static var instance:GameObjectManager = null;
  // the last frame time
  protected var lastFrame:Date;
  // a collection of the GameObjects
  protected var gameObjects:ArrayCollection = new ArrayCollection();
  // a collection where new GameObjects are placed, to avoid adding items
  // to gameObjects while in the gameObjects collection while it is in a loop
  protected var newGameObjects:ArrayCollection = new ArrayCollection();
  // a collection where removed GameObjects are placed, to avoid removing items
  // to gameObjects while in the gameObjects collection while it is in a loop
  protected var removedGameObjects:ArrayCollection = new ArrayCollection();
  protected var collisionMap:Dictionary = new Dictionary();
  static public function get Instance():GameObjectManager
  {
  if ( instance == null )
  instance = new GameObjectManager();
  return instance;
  }
  public function GameObjectManager()
  {
  if ( instance != null )
  throw new Error( "Only one Singleton instance should be instantiated" );
  backBuffer = new BitmapData(Application.application.width, Application.application.height, 
  false);
  }
  public function startup():void
  {
  lastFrame = new Date();
  }
  public function shutdown():void
  {
  shutdownAll();
  }
  public function enterFrame():void
  {
  // Calculate the time since the last frame
  var thisFrame:Date = new Date();
  var seconds:Number = (thisFrame.getTime() - lastFrame.getTime())/1000.0;
  lastFrame = thisFrame;
  removeDeletedGameObjects();
  insertNewGameObjects();
  Level.Instance.enterFrame(seconds);
  checkCollisions();
  // now allow objects to update themselves
  for each (var gameObject:GameObject in gameObjects)
  {
  if (gameObject.inuse)
  gameObject.enterFrame(seconds);
  }
  drawObjects();
  }
  public function click(event:MouseEvent):void
  {
  for each (var gameObject:GameObject in gameObjects)
  {
  if (gameObject.inuse) gameObject.click(event);
  }
  }
  public function mouseDown(event:MouseEvent):void
  {
  for each (var gameObject:GameObject in gameObjects)
  {
  if (gameObject.inuse) gameObject.mouseDown(event);
  }
  }
  public function mouseUp(event:MouseEvent):void
  {
  for each (var gameObject:GameObject in gameObjects)
  {
  if (gameObject.inuse) gameObject.mouseUp(event);
  }
  }
  public function mouseMove(event:MouseEvent):void
  {
  for each (var gameObject:GameObject in gameObjects)
  {
  if (gameObject.inuse) gameObject.mouseMove(event);
  }
  }
  protected function drawObjects():void
  {
  backBuffer.fillRect(backBuffer.rect, clearColor);
  // draw the objects
  for each (var gameObject:GameObject in gameObjects)
  {
  if (gameObject.inuse)
  gameObject.copyToBackBuffer(backBuffer);
  }
  }
  public function addGameObject(gameObject:GameObject):void
  {
  newGameObjects.addItem(gameObject);
  }
  public function removeGameObject(gameObject:GameObject):void
  {
  removedGameObjects.addItem(gameObject);
  }
  protected function shutdownAll():void
  {
  // don't dispose objects twice
  for each (var gameObject:GameObject in gameObjects)
  {
  var found:Boolean = false;
  for each (var removedObject:GameObject in removedGameObjects)
  {
  if (removedObject == gameObject)
  {
  found = true;
  break;
  }
  }
  if (!found)
  gameObject.shutdown();
  }
  }
  protected function insertNewGameObjects():void
  {
  for each (var gameObject:GameObject in newGameObjects)
  {
  for (var i:int = 0; i  gameObject.zOrder ||
  gameObjects.getItemAt(i).zOrder == -1)
  break;
  }
  gameObjects.addItemAt(gameObject, i);
  }
  newGameObjects.removeAll();
  }
  protected function removeDeletedGameObjects():void
  {
  // insert the object acording to it's z position
  for each (var removedObject:GameObject in removedGameObjects)
  {
  var i:int = 0;
  for (i = 0; i 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  Read more: http://lp2.fanqiang.org/browse.php?
  u=Oi8vd3d3LmJyaWdodGh1Yi5jb20vaW50ZXJuZXQvd2ViLWRl dmVsb3BtZW50L2FydGljbGVzLzExODg5LmFzcHg%
  2FcD00&b=5#ixzz0Q6b4QzFf
  复制代码任意两个物体只需要调用一次addCollidingPair方法。玩家飞船将与敌人检测碰撞,敌人将和子
  弹进行碰撞,玩家飞船也将与敌人进行碰撞。
  接下来我们我们要更新Player, Weapon和Enemy类的collisionName和响应碰撞。接下来我们看Player类
  Player.aspackage
  {
  import flash.events.*;
  import flash.geom.*;
  import mx.core.*;
  public class Player extends GameObject
  {
  protected static const TimeBetweenShots:Number = 0.25;
  protected var shooting:Boolean = false;
  protected var timeToNextShot:Number = 0;
  public function Player()
  {
  }
  public function startupPlayer():void
  {
  startupGameObject(ResourceManager.BrownPlaneGraphi cs, new Point
  (Application.application.width / 2, Application.application.height / 2), 
  ZOrders.PlayerZOrder);
  shooting = false;
  timeToNextShot = 0;
  this.collisionName = CollisionIdentifiers.PLAYER;
  }
  override public function shutdown():void
  {
  super.shutdown();
  }
  override public function enterFrame(dt:Number):void
  {
  super.enterFrame(dt);
  timeToNextShot -= dt;
  if (timeToNextShot  Application.application.width - graphics.bitmap.width)
  position.x = Application.application.width - graphics.bitmap.width;
  if (position.y  Application.application.height - graphics.bitmap.height )
  position.y = Application.application.height - graphics.bitmap.height ;
  }
  override public function mouseDown(event:MouseEvent):void
  {
  shooting = true;
  }
  override public function mouseUp(event:MouseEvent):void
  {
  shooting = false;
  }
  override public function collision(other:GameObject):void
  {
  Level.Instance.levelEnd = true;
  this.shutdown();
  }
  }
  }
  Read more: http://lp2.fanqiang.org/browse.php?
  u=Oi8vd3d3LmJyaWdodGh1Yi5jb20vaW50ZXJuZXQvd2ViLWRl dmVsb3BtZW50L2FydGljbGVzLzExODg5LmFzcHg%
  2FcD01&b=5#ixzz0Q6eJzeNp
  复制代码有两处改变让其对碰撞检测系统进行适应。第一个是startup方法中设置了collisionName。第二
  个是加入collision方法,这个方法将被GameObjectManager在碰撞发生时调用。这里我们注意,Level在
  设置levelEnd为true后应当停止(玩家挂了),我们还要调用shutdown来去掉玩家。
  除了levelEnd的改变外,Enemy和Weapon类完全一致,这里我(作者,不是译者)偷懒就不再展示了。
  最后是Level类的改变,如下: 
  Level.aspackage
  {
  import flash.events.*;
  import flash.geom.*;
  import flash.media.*;
  import flash.net.*;
  import flash.utils.*;
  import mx.collections.ArrayCollection;
  import mx.core.*;
  public class Level
  {
  protected static var instance:Level = null;
  protected static const TimeBetweenLevelElements:Number = 2;
  protected static const TimeBetweenEnemies:Number = 3;
  protected static const TimeBetweenClouds:Number = 2.5;
  protected static const TimeToLevelEnd:Number = 2;
  protected var timeToNextLevelElement:Number = 0;
  protected var levelElementGraphics:ArrayCollection = new ArrayCollection();
  protected var timeToNextEnemy:Number = 0;
  protected var enemyElementGraphics:ArrayCollection = new ArrayCollection();
  protected var timeToNextCloud:Number = 0;
  protected var timeToLevelEnd:Number = 0;
  public var levelEnd:Boolean = false;
  static public function get Instance():Level
  {
  if ( instance == null )
  instance = new Level();
  return instance;
  }
  public function Level(caller:Function = null )
  {
  if ( Level.instance != null )
  throw new Error( "Only one Singleton instance should be instantiated" );
  levelElementGraphics. addItem(ResourceManager.SmallIslandGraphics);
  levelElementGraphics. addItem(ResourceManager.BigIslandGraphics);
  levelElementGraphics. addItem(ResourceManager.VolcanoIslandGraphics);
  enemyElementGraphics. addItem(ResourceManager.SmallBluePlaneGraphics);
  enemyElementGraphics. addItem(ResourceManager.SmallGreenPlaneGraphics);
  enemyElementGraphics. addItem(ResourceManager.SmallWhitePlaneGraphics);
  }
  public function startup():void
  {
  timeToNextLevelElement = 0;
  new Player().startupPlayer();
  timeToLevelEnd = TimeToLevelEnd;
  levelEnd = false;
  }
  public function shutdown():void
  {
  }
  public function enterFrame(dt:Number):void
  {
  // add a background element
  timeToNextLevelElement -= dt;
  if (timeToNextLevelElement <= 0)
  {
  timeToNextLevelElement = TimeBetweenLevelElements;
  var graphics:GraphicsResource = levelElementGraphics.getItemAt(MathUtils.randomInt eger(0, 
  levelElementGraphics.length)) as GraphicsResource;
  var backgroundLevelElement:BackgroundLevelElement = BackgroundLevelElement.pool.ItemFromPool 
  as BackgroundLevelElement;
  backgroundLevelElement.startupBackgroundLevelEleme nt(
  graphics,
  new Point(Math.random() * Application.application.width, -graphics.bitmap.height),
  ZOrders.BackgoundZOrder,
  50);
  }
  // add an emeny
  timeToNextEnemy -= dt;
  if (timeToNextEnemy <= 0)
  {
  timeToNextEnemy = TimeBetweenEnemies;
  var enemygraphics:GraphicsResource = enemyElementGraphics.getItemAt(MathUtils.randomInt eger
  (0, enemyElementGraphics.length)) as GraphicsResource;
  var enemy:Enemy = Enemy.pool.ItemFromPool as Enemy;
  enemy.startupBasicEnemy(
  enemygraphics,
  new Point(Math.random() * Application.application.width, -enemygraphics.bitmap.height),
  55);
  }
  // add cloud
  timeToNextCloud -= dt;
  if (timeToNextCloud <= dt)
  {
  timeToNextCloud = TimeBetweenClouds;
  var cloudBackgroundLevelElement:BackgroundLevelElement = 
  BackgroundLevelElement.pool.ItemFromPool as BackgroundLevelElement;
  cloudBackgroundLevelElement. startupBackgroundLevelElement(
  ResourceManager.CloudGraphics,
  new Point(Math.random() * Application.application.width, -
  ResourceManager.CloudGraphics.bitmap.height),
  ZOrders.CloudsBelowZOrder,
  75);
  }
  if (levelEnd)
  timeToLevelEnd -= dt;
  if (timeToLevelEnd <= 0)
  Application.application.currentState = "MainMenu";
  }
  }
  }
  Read more: http://lp2.fanqiang.org/browse.php?
  u=Oi8vd3d3LmJyaWdodGh1Yi5jb20vaW50ZXJuZXQvd2ViLWRl dmVsb3BtZW50L2FydGljbGVzLzExODg5LmFzcHg%
  2FcD02&b=5#ixzz0Q6g1pVM4
  复制代码主要改变就是当玩家挂掉时通过levelEnd通知Level。当被设置为true时,enterFrame中使用
  timeToLevelEnd属性,进行倒数,当timeToLevelEnd为0时,state回到MainMenu(菜单画面)。
  碰撞检测在任何一个动作游戏中都很重要。我们目前用得比较简单,不过很有效。不幸的是,现在击中敌
  机只会简单的消失,在第7部分中,我们将加入动画效果。
  结果: http://flexfighters.sourceforge.net/flexfighters6. html
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics