[新闻资讯] 使用Flex和Actionscript开发Flash游戏——设定关卡

来源:互联网 发布:手机热点软件 编辑:程序博客网 时间:2024/05/22 07:05

  • 资讯类型: 翻译
  • 来源页面: http://www.brighthub.com/internet/web-development/articles/12614.aspx
  • 资讯原标题: Flash Game Development with Flex and Actionscript - Defining a Level
  • 资讯原作者: Matthew Casperson

    我的评论:倒数第二篇~
    对这篇文你有啥看法,跟贴说说吧!欢迎口水和板砖,哈哈。欢迎大家和我们一同分享更多资讯。


    这一部分,我们要实现一个系统,来定义游戏的关卡。

    这里首先指出,我们所指的关卡,就是一个无限随机出现敌人的关卡。很显然,这个关卡不需要开发人员考虑合适游戏会结束。为题提供一个关卡结构的设计,我们需要对关卡进行统一的架构设计。听起来很有挑战,但是我们有很多选择,每个选择均有其各自的优缺点。

    第一个想法就是使用xml来定义关卡。Actionscript对XML支持非常好,其自身就拥有XML类型变量。同样支持对XML进行遍历。使用XML的不便之处是你需要定义和解释XML中的节点。例如,你用一个节点定义了敌人位置。为了在游戏中实现效果,首先需要解析、保存xml,然后传值到方程中,才能实际的创建一个敌人。虽然不难,但是确实很繁琐。

    幸好,Actionscript为我们提供了其它的可能。Function对象可以把方法看作一个对象,能够像其它object那样被调用和传递。甚至,我们可以传递匿名方法到Function对象的构造函数中。我们可以使用这个方法直接创建新的敌人对象。这个敌人对象直接存储在Function对象中,在游戏的level中被直接调用。听起来有点复杂,但是当你看到代码就会明确很多了。

    首先我们需要创建一个类:LevelDefinitionElement.

    LevelDefinitionElement.as
    1. package
    2. {
    3. public class LevelDefinitionElement
    4. {
    5. public var time:Number = 0;
    6. public var func:Function = null;
    7. public function LevelDefinitionElement(time:Number, func:Function)
    8. {
    9. this.time = time;
    10. this.func = func;
    11. }
    12. static public function sort(objectA:LevelDefinitionElement, objectB:LevelDefinitionElement):int
    13. {
    14. if (objectA.time < objectB.time) return -1;
    15. if (objectA.time == objectB.time) return 0;
    16. return 1;
    17. }
    18. }
    19. }
    复制代码
    创建这个类的目的是保存Function对象,这个对象会在level经历了一个固定时间后被调用。例如:你希望游戏开始10秒后出现一架敌机。

    这个类有两个属性:time和func。time定义了func对象被调用的时间点。func属性保存了方法对象。这个方法能够做很多事情:创建一个敌人、创建一个背景元素、创建一个音效等等。事实上,这个属性可以赋值为任何方法,我们可以对关卡的结构进行非常灵活的定义。

    sort方法用来对LevelDefinitionElements数组排序,这样做是为了缩短敌机的出现时间(如果前后敌机设定出现的时间过长)。

    LevelDefinitions类也起到了容器的作用,许多具体的LevelDefinitionElements需要定义来完成一个完整的游戏。请看下面的代码:

    LevelDefinitions.as
    1. package
    2. {
    3. import flash.geom.*;
    4. import flash.utils.Dictionary;
    5. public class LevelDefinitions
    6. {
    7.     protected static var instance:LevelDefinitions = null;
    8.     protected var levelDefinitions:Dictionary = new Dictionary();
    9.     static public function get Instance():LevelDefinitions
    10. {
    11. if ( instance == null )
    12.     instance = new LevelDefinitions();
    13.     return instance;
    14. }
    15. public function LevelDefinitions()
    16. {
    17. }
    18. public function addLevelDefinition(levelID:int, element:LevelDefinitionElement):void
    19. {
    20.     if (levelDefinitions[levelID] == null)
    21.     levelDefinitions[levelID] = new Array();
    22.     (levelDefinitions[levelID] as Array).push(element);
    23.     levelDefinitions[levelID].sort(LevelDefinitionElement.sort);
    24. }
    25. public function getNextLevelDefinitionElements(levelID:int, lastTime:Number):Array
    26. {
    27.     var returnArray:Array = new Array();
    28.     var nextTime:Number = -1;
    29.     if (levelDefinitions[levelID] != null)
    30.     {
    31.         for each (var levelDefElement:LevelDefinitionElement in levelDefinitions[levelID])
    32.         {
    33.         if (levelDefElement.time > lastTime && nextTime == -1)
    34.         {
    35.             returnArray.push(levelDefElement);
    36.             nextTime = levelDefElement.time;
    37.         }
    38.         else if (levelDefElement.time == nextTime)
    39.         {
    40.             returnArray.push(levelDefElement);
    41.         }
    42.         else if (levelDefElement.time > nextTime && nextTime != -1)
    43.             break;
    44.         }
    45.     }
    46.     return returnArray.length == 0?null:returnArray;
    47. }
    48. public function getNextLevelID(levelID:int):int
    49. {
    50.     if (levelDefinitions[levelID + 1] == null) return 0;
    51.     return levelID + 1;
    52. }
    53. public function startup():void
    54. {
    55.     GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.PLAYER, CollisionIdentifiers.ENEMY);
    56.     GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYERWEAPON);
    57.     GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.PLAYER, CollisionIdentifiers.ENEMYWEAPON);
    58.     LevelDefinitions.Instance.addLevelDefinition(
    59. 1,
    60. new LevelDefinitionElement(
    61. 1,
    62. function():void {(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
    63. ResourceManager.SmallBluePlaneGraphics,
    64. new Point(100, -ResourceManager. SmallBluePlaneGraphics.bitmap.height),
    65. 55);}));
    66.     LevelDefinitions.Instance.addLevelDefinition(
    67. 1,
    68. new LevelDefinitionElement(
    69. 4,
    70. function():void
    71. {
    72. for each (var xPos:int in [150, 350])
    73. {
    74. (Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
    75. ResourceManager.SmallBluePlaneGraphics,
    76. new Point(xPos, -ResourceManager. SmallBluePlaneGraphics.bitmap.height),
    77. 55);
    78. }
    79. }
    80. ));
    81.     LevelDefinitions.Instance.addLevelDefinition(
    82. 1,
    83. new LevelDefinitionElement(
    84. 5,
    85. function():void {(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
    86. ResourceManager.SmallBluePlaneGraphics,
    87. new Point(500, -ResourceManager. SmallBluePlaneGraphics.bitmap.height),
    88. 55);}));
    89.     LevelDefinitions.Instance.addLevelDefinition(
    90. 2,
    91. new LevelDefinitionElement(
    92. 1,
    93. function():void {(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
    94. ResourceManager.SmallGreenPlaneGraphics,
    95. new Point(100, -ResourceManager. SmallGreenPlaneGraphics.bitmap.height),
    96. 55);}));
    97.     LevelDefinitions.Instance.addLevelDefinition(
    98. 2,
    99. new LevelDefinitionElement(
    100. 3,
    101. function():void
    102. {
    103. for each (var xPos:int in [150, 350])
    104. {
    105. (Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
    106. ResourceManager.SmallGreenPlaneGraphics,
    107. new Point(xPos, -ResourceManager. SmallGreenPlaneGraphics.bitmap.height),
    108. 55);
    109. }
    110. }
    111. ));
    112.     LevelDefinitions.Instance.addLevelDefinition(
    113. 2,
    114. new LevelDefinitionElement(
    115. 5,
    116. function():void {(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
    117. ResourceManager.SmallGreenPlaneGraphics,
    118. new Point(500, -ResourceManager. SmallGreenPlaneGraphics.bitmap.height),
    119. 55);}));
    120. }
    121. public function shutdown():void
    122. {
    123. }
    124. }
    125. }
    复制代码
    除了单件模式实现所需属性外只有一个其它属性:levelDefinitions。这个一个字典(dictionary),key是level ID(例如:level 1,2,3等等),值value是LevelDefinitionElements数组。记住,一个LevelDefinitionElement定义了一个经过特定时间可调用的方法。所以,levelDefinitions[1][0].func()就是调用了预先存在里面的方法——记住他们是以time属性升序排列的(针对level 1)。

    我们看一下startup方法,就更加明晰了。让我们设置一个敌人的位置。
    1. LevelDefinitions.Instance.addLevelDefinition(
    2. 1,
    3. new LevelDefinitionElement(
    4. 4,
    5. function():void
    6. {
    7. for each (var xPos:int in [150, 350])
    8. {
    9. (Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
    10. ResourceManager.SmallBluePlaneGraphics,
    11. new Point(xPos, -ResourceManager.SmallBluePlaneGraphics.bitmap.height),
    12. 55);
    13. }
    14. }
    15. ));
    复制代码
    我们进行了一次调用在addLevelDefinition中,实际上仅仅是添加了一个LevelDefinitionElement到levelDefinitions字典中。第一个传递的参数是level ID。我们设置为1,就是我们在第一关中添加的。

    第二个参数是一个LevelDefinitionElement。第一个变量是LevelDefinitionElement执行的间隔时间。我们设置为4,就是说LevelDefinitionElement将在进入关卡后4秒执行。

    我们传入的第二个变量是一个匿名方法。就是说,这个方法没有名字,只能通过Function对象调用。其它与普通代码一样。从代码中可以看到,我们创建了2个敌机。

    效果就是,进入游戏4秒后,画面会出现2个敌机。

    一旦你脑中清楚了方法就是一个对象,就会发现这样做使我们更加简单的创建关卡结构。我们没有采用XML格式,尽管XML可以直接解析。我们采用代码的方式创建敌机,当然,方法都是无限可能的~

    (一个小的变化是我们把colliding GameObject pairs的定义移到了startup方法中。保证对游戏的“定义”全部在一处)。当然,我们需要这些方法在正确的时候被调用。下面是代码:

    (待续)
    1

    评分次数

    • 达达

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

    订阅 TOP

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

    香坛护法

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

    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.collections.ArrayCollection;
    9. import mx.core.*;
    10. public class Level
    11. {
    12. protected static var instance:Level = null;
    13. protected static const TimeBetweenLevelElements:Number = 2;
    14. protected static const TimeBetweenClouds:Number = 2.5;
    15. protected static const TimeToLevelEnd:Number = 2;
    16. protected var nextDefinitions:Array = null;
    17. protected var levelID:int = 0;
    18. protected var totalTime:Number = 0;
    19. protected var timeToNextLevelElement:Number = 0;
    20. protected var levelElementGraphics:ArrayCollection = new ArrayCollection();
    21. protected var timeToNextCloud:Number = 0;
    22. protected var timeToLevelEnd:Number = 0;
    23. protected var backgroundMusic:SoundChannel = null;
    24. public var levelEnd:Boolean = false;
    25. static public function get Instance():Level
    26. {
    27. if ( instance == null )
    28. instance = new Level();
    29. return instance;
    30. }
    31. public function Level()
    32. {
    33. levelElementGraphics.addItem(ResourceManager. SmallIslandGraphics);
    34. levelElementGraphics.addItem(ResourceManager. BigIslandGraphics);
    35. levelElementGraphics.addItem(ResourceManager. VolcanoIslandGraphics);
    36. }
    37. public function startup(levelID:int):void
    38. {
    39. timeToNextLevelElement = 0;
    40. new Player().startupPlayer();
    41. timeToLevelEnd = TimeToLevelEnd;
    42. levelEnd = false;
    43. backgroundMusic = ResourceManager.Track1FX.play(0, int.MAX_VALUE);
    44. this.totalTime = 0;
    45. this.levelID = levelID;
    46. nextDefinitions = LevelDefinitions.Instance. getNextLevelDefinitionElements(levelID, 0);
    47. }
    48. public function shutdown():void
    49. {
    50. backgroundMusic.stop();
    51. backgroundMusic = null;
    52. }
    53. public function enterFrame(dt:Number):void
    54. {
    55. totalTime += dt;
    56. if (nextDefinitions == null)
    57. {
    58. if (Enemy.pool.NumberOfActiveObjects == 0)
    59. levelEnd = true;
    60. }
    61. else
    62. {
    63. var nextLevelDefTime:Number = (nextDefinitions[0] as LevelDefinitionElement).time;
    64. if (totalTime >= nextLevelDefTime)
    65. {
    66. for each (var levelDefElement:LevelDefinitionElement in nextDefinitions)
    67. levelDefElement.func();
    68. nextDefinitions = LevelDefinitions.Instance.getNextLevelDefinitionElements(levelID, nextLevelDefTime);
    69. }
    70. }
    71. // add a background element
    72. timeToNextLevelElement -= dt;
    73. if (timeToNextLevelElement <= 0)
    74. {
    75. timeToNextLevelElement = TimeBetweenLevelElements;
    76. var graphics:GraphicsResource = levelElementGraphics.getItemAt(MathUtils.randomInteger(0, levelElementGraphics.length)) as GraphicsResource;
    77. var backgroundLevelElement:BackgroundLevelElement = BackgroundLevelElement.pool.ItemFromPool as BackgroundLevelElement;
    78. backgroundLevelElement.startupBackgroundLevelElement(
    79. graphics,
    80. new Point(Math.random() * Application.application.width, -graphics.bitmap.height),
    81. ZOrders.BackgoundZOrder,
    82. 50);
    83. }
    84. // add cloud
    85. timeToNextCloud -= dt;
    86. if (timeToNextCloud <= dt)
    87. {
    88. timeToNextCloud = TimeBetweenClouds;
    89. var cloudBackgroundLevelElement:BackgroundLevelElement = BackgroundLevelElement.pool.ItemFromPool as BackgroundLevelElement;
    90. cloudBackgroundLevelElement.startupBackgroundLevelElement(
    91. ResourceManager.CloudGraphics,
    92. new Point(Math.random() * Application.application.width, -ResourceManager.CloudGraphics.bitmap.height),
    93. ZOrders.CloudsBelowZOrder,
    94. 75);
    95. }
    96. if (levelEnd)
    97. {
    98. timeToLevelEnd -= dt;
    99. var scale:Number = timeToLevelEnd / TimeToLevelEnd;
    100. if (scale < 0) scale = 0;
    101. var transform:SoundTransform = backgroundMusic.soundTransform;
    102. transform.volume = scale;
    103. backgroundMusic.soundTransform = transform;
    104. }
    105. if (timeToLevelEnd <= 0)
    106. Application.application.currentState = "LevelEnd";
    107. }
    108. }
    109. }
    复制代码
    我是小辛~
    新手看过来:天地会入会必读
     
    回复 引用

    TOP

      

    香坛护法

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

    我们把所有关于随机创建敌人的代码和属性都移除掉了。尽管最终关卡将在LevelDefinitions类中定义,不过目前我们还是随机产生BackgroundElements交由Level管理。

    一共添加了3个新属性:nextDefinitions, levelID和totalTime。nextDefinitions属性存储了一个由LevelDefinitionElements组成的数组,表示下一个将要执行的对象(Function对象)。在enterFrame方法中,Level类通过LevelDefinitions的getNextLevelDefinitionElements方法请求下一批要执行的LevelDefinitionElements。当时间到达,LevelDefinitionElements执行,同时请求了下一批要执行的对象。当没有LevelDefinitionElements(例如:nextDefinitions == null)时Level类等待所有敌人被击毁,然后关卡结束(设置levelEnd为true)。

    levelID属性为当前的关卡。与addLevelDefinition方法中的相同。

    totalTime属性表示当前关卡已经执行的时间。这是nextDefinitions适时请求下一批LevelDefinitionElements的保障。

    定义了顺序的关卡结构之后,我们就有了为游戏添加更多很酷的东西的能力了。

    最终结果:http://flexfighters.sourceforge.net/flexfighters9.html
    源码:https://sourceforge.net/project/ ... p;release_id=634503