使用Flex和Actionscript开发Flash游戏―(十)


使用Flex和Actionscript开发Flash游戏―(十)
2010年07月11日
  这一部分,我们要加入一个可以滚动的重复的背景。
  第9部分中,我们添加了关卡的按时调用功能构成的关卡结构。这个绘制主要用来显示界面上的敌机,但是对于背景的显示并不是很有用。这个部分,我们要绘制预定义的可重复的背景。
  重复背景由一系列很小的可重复的图片排列而成,在这里,就是按网格排列。这样做有几个优点,最重要的就是可以减小内存的需求。用一张很小的图像来渲染背景,每一关能减小内存好几兆,同时保留了高细节的呈现。只不过背景并不响应单块的互动事件,只是被关卡载入。与此相对,重复背景占用内存小,设计师同样可以利用这一特点制作出相当好看的背景。  
  
  第一步就是绘制一个可自重复的背景图片。我发现了很多不错的图片 http://lostgarden.com/labels/free%20game%20graphics.html。站点上还有一些其它有趣的资源。  
  
  下一步是找一个关卡编辑器,提供我们交互界面操作。当然也可以自己开发(这个可以再写一系列文章了)。幸好,有人已经做了这个工作。TaT自重复地图编辑器:http://kotisivu.dnainternet.net/ttilli/tilemapedit or/download.htm
  其中的层编辑和xml导出都很好用。
  当然,我们需要在游戏中加代码了~首先是存储背景数据。TiledBackgroundDefinition类来了~看看代码:
   TiledBackgroundDefinition.as
  
package
  {
  public class TiledBackgroundDefinition
  {
  public var tiles:Array = null;
  public var tileScrollRate:Number = 0;
  public var tileWidth:int = 0;
  public var tileHeight:int = 0;
  }
  } 复制代码
  tiles属性是一个多维数组,包含了GraphicsResources的引用,这些用来绘制背景,三维分别表示层、行和列。例如:tiles[1][4][5]指向GraphicsResource到第六列,第五行,第二层――从0开始索引的。tileWidth和tileHeight属性定义了关卡北京的尺寸。tileScrollRate定义了关卡滚动速度。
  现在我们可以存储定义自重复背景了。LevelDefinitions类用来存储定义。让我们看看代码:
   LevelDefinitions.as
  LevelDefinitions.as
  package
  {
  import flash
  .geom.*;
  import flash.utils.*;
  public class LevelDefinitions
  {
  protected static var instance:LevelDefinitions = null;
  protected var levelDefinitions:Dictionary = new Dictionary();
  public var levelTileMaps:Dictionary = new Dictionary();
  static public function get Instance():LevelDefinitions
  {
  if ( instance == null )
  instance = new LevelDefinitions();
  return instance;
  }
  public function LevelDefinitions()
  {
  }
  public function addLevelDefinition(levelID:int, element:LevelDefinitionElement):void
  {
  if (levelDefinitions[levelID] == null)
  levelDefinitions[levelID] = new Array();
  (levelDefinitions[levelID] as Array).push(element);
  levelDefinitions[levelID].sort(LevelDefinitionElem ent.sort);
  }
  public function getNextLevelDefinitionElements(levelID:int, lastTime:Number):Array
  {
  var returnArray:Array = new Array();
  var nextTime:Number = -1;
  if (levelDefinitions[levelID] != null)
  {
  for each (var levelDefElement:LevelDefinitionElement in levelDefinitions[levelID])
  {
  if (levelDefElement.time > lastTime && nextTime == -1)
  {
  returnArray.push(levelDefElement);
  nextTime = levelDefElement.time;
  }
  else if (levelDefElement.time == nextTime)
  {
  returnArray.push(levelDefElement);
  }
  else if (levelDefElement.time > nextTime && nextTime != -1)
  break;
  }
  }
  return returnArray.length == 0?null:returnArray;
  }
  public function getNextLevelID(levelID:int):int
  {
  if (levelDefinitions[levelID + 1] == null) return 0;
  return levelID + 1;
  }
  public function startup():void
  {
  GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.PLAYER, CollisionIdentifiers.ENEMY);
  GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYERWEAPON);
  GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.PLAYER, CollisionIdentifiers.ENEMYWEAPON);
  defineLevel1();
  defineLevel2();
  }
  public function shutdown():void
  {
  }
  protected function defineLevel1():void
  {
  var level1Tiles:TiledBackgroundDefinition = new TiledBackgroundDefinition();
  levelTileMaps[1] = level1Tiles;
  level1Tiles.tileScrollRate = 25;
  level1Tiles.tileHeight = 40;
  level1Tiles.tileWidth = 40;
  level1Tiles.tiles =
  [
  [
  [ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1]
  ...
  ,[ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1, ResourceManager.GreenGraphicsID1]
  ]
  ,[
  [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
  ...
  ,[null, ResourceManager.GreenGraphicsID62, ResourceManager.GreenGraphicsID63, ResourceManager.GreenGraphicsID64, null, ResourceManager.GreenGraphicsID48, ResourceManager.GreenGraphicsID49, null, null, null, null, null, null, null, null]
  ]
  ];
  LevelDefinitions.Instance.addLevelDefinition(
  1,
  new LevelDefinitionElement(
  4,
  function():void
  {
  for each (var xPos:int in [150, 350])
  {
  (Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
  ResourceManager.SmallBluePlaneGraphics,
  new Point(xPos, -ResourceManager. SmallBluePlaneGraphics.bitmap.height),
  55);
  }
  }
  ));
  ...
  }
  protected function defineLevel2():void
  {
  ...
  }
  }
  }
  我们添加了一个新的属性levelTileMaps,这是一个Dictionary类型,用来将TiledBackgroundDefinitions映射至LevelID(和第9部分levelDefinitions属性类似)。
  同样的,我们增加了两个新方法:defineLevel1和defineLevel2。这两个方法用于分别定义levels,看上去新的自重复的背景定义有点累赘。在这两个方法中,我们创建TiledBackgroundDefinition对象,初始化它们并赋值到levelTileMaps属性上。
  当你使用TaT创建关卡时,会得到两个很重要的文件:tileset.xml(定义了单个的字重复图像)和一个 你关卡的名字.xml文件(定义了图片在关卡中的排列情况)。我们要把 你关卡的名字.xml文件的数据放到tiles数组中。
  你可能会奇怪,怎样才能把xml数据存到多维数组中。简单说,我创建另一个程序,将xml转换成Actionscript代码,然后创建数组。为什么不选择在程序中解析XML呢?理由和我不选择用xml定义关卡结构是一样的:解析xml并转换数据类型是一个很复杂的过程,而转换xml为actionscript要简单很多。这里我就不讲解转换程序的代码了,你可以在这里下载到:http://flexfighters.svn.sourcefo ... /TatResourceParser/
  TaT编辑器创建的第二个xml定义了组成关卡的单个的自重复图像。使用上面的程序,我们也可以将其转换成actionscript代码,嵌入图像然后用ResourceManager创建与之对应的GraphicsResource对象。新的代码重复部分很多,下面我只写一小部分不一样的:
   ResourceManager.as (新代码示例)
  [Embed(source="../media/Green26.png")]
  public static var GreenID65:Class;
  public static var GreenGraphicsID65:GraphicsResource = new GraphicsResource(new GreenID65());
  [Embed(source="../media/Green5.png")]
  public static var GreenID11:Class;
  public static var GreenGraphicsID11:GraphicsResource = new GraphicsResource(new GreenID11(), 1, 1, new Rectangle(0, 0, 40, 40));
  public static var GreenGraphicsID12:GraphicsResource = new GraphicsResource(new GreenID11(), 1, 1, new Rectangle(40, 0, 40, 40));
  public static var GreenGraphicsID17:GraphicsResource = new GraphicsResource(new GreenID11(), 1, 1, new Rectangle(0, 40, 40, 40));
  public static var GreenGraphicsID18:GraphicsResource = new GraphicsResource(new GreenID11(), 1, 1, new Rectangle(40, 40, 40, 40));
  代码与其它资源定义相同,除了传给ResourceManager新的参数。这是为了适应TaT引用的结构――加入关卡的图片比自重复的图片尺寸大。所以,例如一个字重复图片大小是40x40像素,但是整个树的图像可以是40x80像素。在这种情况下,树的图片要引用两个独立的图片,按照定义,一个在上面,一个在下面。为了让GraphicsResouce指向相同大小的区域,我们可以将其指向相同的自重复图片。
  现在,我们可以定义并保存自重复图像了。接下来就是要绘制了。请看代码:
   TiledBackground.as
  package
  {
  import flash.display.*;
  import flash.geom.*;
  import mx.collections.*;
  import mx.core.*;
  public class TiledBackground extends BaseObject
  {
  public var scrolling:Boolean = true;
  protected var yOffset:Number = 0;
  protected var definition:TiledBackgroundDefinition = null;
  static public var pool:ResourcePool = new ResourcePool(NewTiledBackground);
  static public function NewTiledBackground():TiledBackground
  {
  return new TiledBackground();
  }
  public function TiledBackground()
  {
  super();
  }
  public function startupTiledBackground(definition:TiledBackgroundD efinition):void
  {
  super.startupBaseObject(ZOrders.BACKGROUNDZORDER);
  this.definition = definition;
  this.yOffset = 0;
  this.scrolling = true;
  }
  override public function shutdown():void
  {
  super.shutdown();
  }
  override public function enterFrame(dt:Number):void
  {
  if (scrolling)
  {
  var mapHeight:int = definition.tiles[0].length * definition.tileHeight;
  var mapOverlap:int = mapHeight - Application.application.height;
  yOffset += definition.tileScrollRate * dt;
  if (yOffset > mapOverlap)
  {
  scrolling = false;
  yOffset = mapOverlap;
  }
  }
  }
  override public function copyToBackBuffer(db:BitmapData):void
  {
  var startRow:int = yOffset / definition.tileHeight;
  var startRowNumber:Number = yOffset / definition.tileHeight;
  var startRowHeight:int = definition.tileHeight * (startRowNumber - startRow);
  var drawnHeight:int = 0;
  var drawnWidth:int = 0;
  var layer:int = 0;
  var row:int = startRow;
  var col:int = 0;
  // loop through each layer
  for (layer = 0; layer = 0 ; --row)
  {
  // loop through each column of the current row
  for (col = 0; col = Application.application.width)
  break;
  }
  drawnWidth = 0;
  drawnHeight += definition.tileHeight;
  if (drawnHeight >= Application.application.height + definition.tileHeight)
  break;
  }
  drawnHeight = 0;
  }
  }
  }
  }
  
  

猜你喜欢

转载自pc519pc.iteye.com/blog/1571792
今日推荐