[新闻资讯] 使用Flex和Actionscript开发Flash游戏——重复背景绘制

来源:互联网 发布:博客天下 知乎 编辑:程序博客网 时间:2024/05/31 18:45

[新闻资讯] 使用Flex和Actionscript开发Flash游戏——重复背景绘制

Flex, Flash, Actionscript, 游戏, 开发
  • 资讯类型: 翻译
  • 来源页面: http://www.brighthub.com/internet/web-development/articles/12615.aspx
  • 资讯原标题: Flash Game Development with Flex and Actionscript - Tiled Background Rendering
  • 资讯原作者: Matthew Casperson

    我的评论:最后一篇~作者的架构的思想是很好的~涵盖了游戏的诸多方面,很细致。
    对这篇文你有啥看法,跟贴说说吧!欢迎口水和板砖,哈哈。欢迎大家和我们一同分享更多资讯。


    这一部分,我们要加入一个可以滚动的重复的背景。

    第9部分中,我们添加了关卡的按时调用功能构成的关卡结构。这个绘制主要用来显示界面上的敌机,但是对于背景的显示并不是很有用。这个部分,我们要绘制预定义的可重复的背景。

    重复背景由一系列很小的可重复的图片排列而成,在这里,就是按网格排列。这样做有几个优点,最重要的就是可以减小内存的需求。用一张很小的图像来渲染背景,每一关能减小内存好几兆,同时保留了高细节的呈现。只不过背景并不响应单块的互动事件,只是被关卡载入。与此相对,重复背景占用内存小,设计师同样可以利用这一特点制作出相当好看的背景。

    第一步就是绘制一个可自重复的背景图片。我发现了很多不错的图片 http://lostgarden.com/labels/free%20game%20graphics.html。站点上还有一些其它有趣的资源。

    下一步是找一个关卡编辑器,提供我们交互界面操作。当然也可以自己开发(这个可以再写一系列文章了)。幸好,有人已经做了这个工作。TaT自重复地图编辑器:http://kotisivu.dnainternet.net/ttilli/tilemapeditor/download.htm
    其中的层编辑和xml导出都很好用。

    当然,我们需要在游戏中加代码了~首先是存储背景数据。TiledBackgroundDefinition类来了~看看代码:

    TiledBackgroundDefinition.as
    1. package
    2. {
    3. public class TiledBackgroundDefinition
    4. {
    5. public var tiles:Array = null;
    6. public var tileScrollRate:Number = 0;
    7. public var tileWidth:int = 0;
    8. public var tileHeight:int = 0;
    9. }
    10. }
    复制代码
    tiles属性是一个多维数组,包含了GraphicsResources的引用,这些用来绘制背景,三维分别表示层、行和列。例如:tiles[1][4][5]指向GraphicsResource到第六列,第五行,第二层——从0开始索引的。tileWidth和tileHeight属性定义了关卡北京的尺寸。tileScrollRate定义了关卡滚动速度。

    现在我们可以存储定义自重复背景了。LevelDefinitions类用来存储定义。让我们看看代码:
    LevelDefinitions.as
    1. LevelDefinitions.as
    2. package
    3. {
    4. import flash.geom.*;
    5. import flash.utils.*;
    6. public class LevelDefinitions
    7. {
    8. protected static var instance:LevelDefinitions = null;
    9. protected var levelDefinitions:Dictionary = new Dictionary();
    10. public var levelTileMaps:Dictionary = new Dictionary();
    11. static public function get Instance():LevelDefinitions
    12. {
    13. if ( instance == null )
    14. instance = new LevelDefinitions();
    15. return instance;
    16. }
    17. public function LevelDefinitions()
    18. {
    19. }
    20. public function addLevelDefinition(levelID:int, element:LevelDefinitionElement):void
    21. {
    22. if (levelDefinitions[levelID] == null)
    23. levelDefinitions[levelID] = new Array();
    24. (levelDefinitions[levelID] as Array).push(element);
    25. levelDefinitions[levelID].sort(LevelDefinitionElement.sort);
    26. }
    27. public function getNextLevelDefinitionElements(levelID:int, lastTime:Number):Array
    28. {
    29. var returnArray:Array = new Array();
    30. var nextTime:Number = -1;
    31. if (levelDefinitions[levelID] != null)
    32. {
    33. for each (var levelDefElement:LevelDefinitionElement in levelDefinitions[levelID])
    34. {
    35. if (levelDefElement.time > lastTime && nextTime == -1)
    36. {
    37. returnArray.push(levelDefElement);
    38. nextTime = levelDefElement.time;
    39. }
    40. else if (levelDefElement.time == nextTime)
    41. {
    42. returnArray.push(levelDefElement);
    43. }
    44. else if (levelDefElement.time > nextTime && nextTime != -1)
    45. break;
    46. }
    47. }
    48. return returnArray.length == 0?null:returnArray;
    49. }
    50. public function getNextLevelID(levelID:int):int
    51. {
    52. if (levelDefinitions[levelID + 1] == null) return 0;
    53. return levelID + 1;
    54. }
    55. public function startup():void
    56. {
    57. GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.PLAYER, CollisionIdentifiers.ENEMY);
    58. GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYERWEAPON);
    59. GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.PLAYER, CollisionIdentifiers.ENEMYWEAPON);
    60. defineLevel1();
    61. defineLevel2();
    62. }
    63. public function shutdown():void
    64. {
    65. }
    66. protected function defineLevel1():void
    67. {
    68. var level1Tiles:TiledBackgroundDefinition = new TiledBackgroundDefinition();
    69. levelTileMaps[1] = level1Tiles;
    70. level1Tiles.tileScrollRate = 25;
    71. level1Tiles.tileHeight = 40;
    72. level1Tiles.tileWidth = 40;
    73. level1Tiles.tiles =
    74. [
    75. [
    76. [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]
    77. ...
    78. ,[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]
    79. ]
    80. ,[
    81. [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
    82. ...
    83. ,[null, ResourceManager.GreenGraphicsID62, ResourceManager.GreenGraphicsID63, ResourceManager.GreenGraphicsID64, null, ResourceManager.GreenGraphicsID48, ResourceManager.GreenGraphicsID49, null, null, null, null, null, null, null, null]
    84. ]
    85. ];
    86. LevelDefinitions.Instance.addLevelDefinition(
    87. 1,
    88. new LevelDefinitionElement(
    89. 4,
    90. function():void
    91. {
    92. for each (var xPos:int in [150, 350])
    93. {
    94. (Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
    95. ResourceManager.SmallBluePlaneGraphics,
    96. new Point(xPos, -ResourceManager. SmallBluePlaneGraphics.bitmap.height),
    97. 55);
    98. }
    99. }
    100. ));
    101. ...
    102. }
    103. protected function defineLevel2():void
    104. {
    105. ...
    106. }
    107. }
    108. }
    复制代码
    (待续)
    1

    评分次数

    • 达达

    本主题由 达达 于 昨天 12:43 审核通过
    收藏 分享 评分
  • 我是小辛~
     
    回复 引用

    订阅 报告 道具 TOP

     
    立刻订阅天地会人人快报,时刻同步全球RIA技术与资讯热点! RSS订阅地址: http://feeds.feedburner.com/9RIAnews

    香坛护法

    Rank: 4Rank: 4Rank: 4Rank: 4

    我们添加了一个新的属性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 (新代码示例)
    1. [Embed(source="../media/Green26.png")]
    2. public static var GreenID65:Class;
    3. public static var GreenGraphicsID65:GraphicsResource = new GraphicsResource(new GreenID65());
    4. [Embed(source="../media/Green5.png")]
    5. public static var GreenID11:Class;
    6. public static var GreenGraphicsID11:GraphicsResource = new GraphicsResource(new GreenID11(), 1, 1, new Rectangle(0, 0, 40, 40));
    7. public static var GreenGraphicsID12:GraphicsResource = new GraphicsResource(new GreenID11(), 1, 1, new Rectangle(40, 0, 40, 40));
    8. public static var GreenGraphicsID17:GraphicsResource = new GraphicsResource(new GreenID11(), 1, 1, new Rectangle(0, 40, 40, 40));
    9. public static var GreenGraphicsID18:GraphicsResource = new GraphicsResource(new GreenID11(), 1, 1, new Rectangle(40, 40, 40, 40));
    复制代码
    我是小辛~
    新手看过来:天地会入会必读
     
    回复 引用

    报告 道具 TOP

      

    堂口教头

    Rank: 3Rank: 3Rank: 3

    哪位有好的游戏素材提取的方法或经验啊,想找素材来作作实验
    医学教育网
    互联互动实验室
     
    回复 引用

    报告 道具 TOP

      

    香坛护法

    Rank: 4Rank: 4Rank: 4Rank: 4

    代码与其它资源定义相同,除了传给ResourceManager新的参数。这是为了适应TaT引用的结构——加入关卡的图片比自重复的图片尺寸大。所以,例如一个字重复图片大小是40x40像素,但是整个树的图像可以是40x80像素。在这种情况下,树的图片要引用两个独立的图片,按照定义,一个在上面,一个在下面。为了让GraphicsResouce指向相同大小的区域,我们可以将其指向相同的自重复图片。

    现在,我们可以定义并保存自重复图像了。接下来就是要绘制了。请看代码:

    TiledBackground.as
    1. package
    2. {
    3. import flash.display.*;
    4. import flash.geom.*;
    5. import mx.collections.*;
    6. import mx.core.*;
    7. public class TiledBackground extends BaseObject
    8. {
    9. public var scrolling:Boolean = true;
    10. protected var yOffset:Number = 0;
    11. protected var definition:TiledBackgroundDefinition = null;
    12. static public var pool:ResourcePool = new ResourcePool(NewTiledBackground);
    13. static public function NewTiledBackground():TiledBackground
    14. {
    15. return new TiledBackground();
    16. }
    17. public function TiledBackground()
    18. {
    19. super();
    20. }
    21. public function startupTiledBackground(definition:TiledBackgroundDefinition):void
    22. {
    23. super.startupBaseObject(ZOrders.BACKGROUNDZORDER);
    24. this.definition = definition;
    25. this.yOffset = 0;
    26. this.scrolling = true;
    27. }
    28. override public function shutdown():void
    29. {
    30. super.shutdown();
    31. }
    32. override public function enterFrame(dt:Number):void
    33. {
    34. if (scrolling)
    35. {
    36. var mapHeight:int = definition.tiles[0].length * definition.tileHeight;
    37. var mapOverlap:int = mapHeight - Application.application.height;
    38. yOffset += definition.tileScrollRate * dt;
    39. if (yOffset > mapOverlap)
    40. {
    41. scrolling = false;
    42. yOffset = mapOverlap;
    43. }
    44. }
    45. }
    46. override public function copyToBackBuffer(db:BitmapData):void
    47. {
    48. var startRow:int = yOffset / definition.tileHeight;
    49. var startRowNumber:Number = yOffset / definition.tileHeight;
    50. var startRowHeight:int = definition.tileHeight * (startRowNumber - startRow);
    51. var drawnHeight:int = 0;
    52. var drawnWidth:int = 0;
    53. var layer:int = 0;
    54. var row:int = startRow;
    55. var col:int = 0;
    56. // loop through each layer
    57. for (layer = 0; layer < definition.tiles.length; ++layer)
    58. {
    59. // loop through each row
    60. var count:int = 0;
    61. for (row = (definition.tiles[layer] as Array).length - 1 - startRow; row >= 0 ; --row)
    62. {
    63. // loop through each column of the current row
    64. for (col = 0; col < (definition.tiles[layer][row] as Array).length; ++col)
    65. {
    66. var graphics:GraphicsResource = definition.tiles[layer][row][col] as GraphicsResource;
    67. var top:int = Application.application.height - drawnHeight - definition.tileHeight + startRowHeight;
    68. if (graphics != null)
    69. {
    70. db.copyPixels(
    71. graphics.bitmap,
    72. graphics.drawRect,
    73. new Point(
    74. col * definition.tileWidth,
    75. top),
    76. graphics.bitmapAlpha,
    77. new Point(
    78. graphics.drawRect.x,
    79. graphics.drawRect.y),
    80. true);
    81. }
    82. drawnWidth += definition.tileWidth;
    83. if (drawnWidth >= Application.application.width)
    84. break;
    85. }
    86. drawnWidth = 0;
    87. drawnHeight += definition.tileHeight;
    88. if (drawnHeight >= Application.application.height + definition.tileHeight)
    89. break;
    90. }
    91. drawnHeight = 0;
    92. }
    93. }
    94. }
    95. }
    复制代码
    我是小辛~
     
    回复 引用

    报告 道具 TOP

      

    天地会兄弟

    Rank: 2Rank: 2

    不错!用的是魔兽的地图吧?
     
    回复 引用

    报告 道具 TOP

      

    香坛护法

    Rank: 4Rank: 4Rank: 4Rank: 4

    这里要讲解3个属性。scrolling属性标记关卡是否在滚动(true),false表示level已经滚动到了尽头。yOffset属性用来保存level滚动了多少。definition属性保存了到一个自重复background definitions的引用,在Level类中创建。

    enterFrame方法和copyToBackBuffer方法做了大量的工作。等执行enterFrame方法时,TiledBackground根据TiledBackgroundDefinition类(定义了滚动速度)的tileScrollRate属性向下滚动。一旦探测到滚动到了尽头,就把scrolling设置为false然后停止更新yOffset。copyToBackBuffer方法决定在哪里绘制自重复图像。第一次是层的循环,然后是行和列,将它们绘制到正确的位置。

    TiledBackground实例的创建也需要分割老的GameObject类。原先GameObject表示每一个元素都由一个GrphicsResource类实例表示。而现在TiledBackground可能会访问很多很多的GraphicsResources在屏幕上绘制它们。为了适应这个情况,我们创建一个新类叫BaseObject,保存了所有通用的属性,不包括GraphicsResource。TiledBackground和GameObject同时继承自BaseObject,而Player和Enemy保持不变。

    Level类将所有的工作融合到一起,并在合适的地方创建TiledBackground。来看看代码:

    Level.as
    1. package
    2. {
    3. import flash.events.*;
    4. import flash.geom.*;
    5. import flash.media.*;
    6. import flash.net.*;
    7. import flash.utils.*;
    8. import mx.core.*;
    9. public class Level
    10. {
    11. protected static var instance:Level = null;
    12. protected static const TimeBetweenLevelElements:Number = 2;
    13. protected static const TimeBetweenClouds:Number = 2.5;
    14. protected static const TimeToLevelEnd:Number = 2;
    15. protected var nextDefinitions:Array = null;
    16. protected var levelID:int = 0;
    17. protected var totalTime:Number = 0;
    18. protected var timeToNextCloud:Number = 0;
    19. protected var timeToLevelEnd:Number = 0;
    20. protected var backgroundMusic:SoundChannel = null;
    21. public var levelEnd:Boolean = false;
    22. static public function get Instance():Level
    23. {
    24. if ( instance == null )
    25. instance = new Level();
    26. return instance;
    27. }
    28. public function Level()
    29. {
    30. }
    31. public function startup(levelID:int):void
    32. {
    33. new Player().startupPlayer();
    34. timeToLevelEnd = TimeToLevelEnd;
    35. levelEnd = false;
    36. backgroundMusic = ResourceManager.Track1FX.play(0, int.MAX_VALUE);
    37. this.totalTime = 0;
    38. this.levelID = levelID;
    39. nextDefinitions = LevelDefinitions.Instance.getNextLevelDefinitionElements(levelID, 0);
    40. var tileDefinition:TiledBackgroundDefinition = LevelDefinitions.Instance.levelTileMaps[levelID] as TiledBackgroundDefinition;
    41. if (tileDefinition != null)
    42. (TiledBackground.pool.ItemFromPool as TiledBackground).startupTiledBackground(tileDefinition);
    43. }
    44. public function shutdown():void
    45. {
    46. backgroundMusic.stop();
    47. backgroundMusic = null;
    48. }
    49. public function enterFrame(dt:Number):void
    50. {
    51. totalTime += dt;
    52. if (nextDefinitions == null)
    53. {
    54. if (Enemy.pool.NumberOfActiveObjects == 0)
    55. levelEnd = true;
    56. }
    57. else
    58. {
    59. var nextLevelDefTime:Number = (nextDefinitions[0] as LevelDefinitionElement).time;
    60. if (totalTime >= nextLevelDefTime)
    61. {
    62. for each (var levelDefElement:LevelDefinitionElement in nextDefinitions)
    63. levelDefElement.func();
    64. nextDefinitions = LevelDefinitions.Instance.getNextLevelDefinitionElements(levelID, nextLevelDefTime);
    65. }
    66. }
    67. // add cloud
    68. timeToNextCloud -= dt;
    69. if (timeToNextCloud <= dt)
    70. {
    71. timeToNextCloud = TimeBetweenClouds;
    72. var cloudBackgroundLevelElement:BackgroundLevelElement = BackgroundLevelElement.pool.ItemFromPool as BackgroundLevelElement;
    73. cloudBackgroundLevelElement.startupBackgroundLevelElement(
    74. ResourceManager.CloudGraphics,
    75. new Point(Math.random() * Application.application.width, -ResourceManager.CloudGraphics.bitmap.height),
    76. ZOrders.CLOUDSBELOWZORDER,
    77. 75);
    78. }
    79. if (levelEnd)
    80. {
    81. timeToLevelEnd -= dt;
    82. var scale:Number = timeToLevelEnd / TimeToLevelEnd;
    83. if (scale < 0) scale = 0;
    84. var transform:SoundTransform = backgroundMusic.soundTransform;
    85. transform.volume = scale;
    86. backgroundMusic.soundTransform = transform;
    87. }
    88. if (timeToLevelEnd <= 0)
    89. Application.application.currentState = "LevelEnd";
    90. }
    91. }
    92. }
    复制代码
    我是小辛~
     
    回复 引用

    报告 道具 TOP

      

    香坛护法

    Rank: 4Rank: 4Rank: 4Rank: 4

    只用了3行代码来表示创建BackgroundLevelElement。

    加入了自重复背景的绘制后,我们的游戏有了一个好看的背景,同时不会消耗太多的内存。多亏了免费的地图编辑器和资源。

    结果:http://flexfighters.sourceforge.net/flexfighters10.html
    源码:http://sourceforge.net/projects/ ... ers/FlexFighters10/

    【全系列完~】
    原创粉丝点击