读书人

AS3块传输技术显现游戏元素

发布时间: 2012-12-27 10:17:10 作者: rapoo

AS3块传输技术呈现游戏元素

许多类型的游戏,用户体验都依赖于终端可拥有的屏幕像素和移动物体有多快。当让大量的DisplayObject对象动起来时,如MovieClip或Sprite对象,Adobe Flash Player可能在表现上会大大折扣。Flash Player必须遍历显示对象树并为每个基于向量的DisplayObject计算渲染输出,这样会消耗CPU周期成为真正的瓶颈,尤其是低端机。?? ? 许多屏幕动画游戏都是预先载入一张位图,这种技术被称为blitting(块传输)。块传输虽不能解决所有性能问题,但它能使动画运行平滑,统一大多数机器上动画帧频。Blitting术语来自于BitBLT为Xerox Alto计算机日常中创建。BitBLT发音为“bit blit”,主张bit-block (image) transfer图像基于位块传输,是一种采用数张位图并合并成一张位图的技术。在Flash Player中复制位图像素到一张渲染图中比分别地渲染每个DisplayObject更为快速。?? ?在本文中,我描述了软件位块传输技术并提供示例代码,因此你可以在ActionScript中应用它。要求为了满足本文条件,你需要下列软件和文件。
Flash Builder 4 betaFlash Player 9 以上版本示例文件:actionscript_blitting.zip (ZIP, 185 KB)

预备知识?? ? 本文假定你已熟悉Flash Builder 4 和 ActionScript 3项目工作的知识。介绍sprite sheet?? ? ? 一个游戏由许多图形元件组成,例如赛道上的车或森林中的一棵树。在本文中,这些元件都是位图。一组位图放在一个单独的图像文件中称为sprite sheet。例如,一个sprite sheet可能包含一个角色行走动画的所有帧。这个词衍生于sprite,在计算机图形世界中,指的是一张图像或一个大场景中集成的动画。虽然位块传输技术可以使用不同来源的位图数据,但在本文中重点放在sprite sheet。??一个sprite sheet由什么构成??? ? 一个sprite sheet可以对不同大小的位图进行组合。将所有图形元素组装到一个大的图像文件中会减少加载时间(打开和读取一个包含100帧的较大文件比打开读取100个小文件更为快速)并提供压缩的好处。特别是sprite sheet持有同样大小形成一个序列的位图或围绕一个特定游戏元素动画。例如,本文中使用的sprite sheet有五列四行,每格40*40像素,每个都包含着一个brown collector褐色元素(见图1)。

设置ActionScript项目?? ?在你运行示例代码前,你将需要在Flash Builder 4按照以下步骤设置项目。1. ? ? ? ?下载并解压示例文件2. ? ? ? ?选择File > New > ActionScript Project来创建项目3. ? ? ? ?输入ActionScriptBlitting作为项目名称并点击Finish4. ? ? ? ?从示例文件中复制下列文件和文件到项目默认包中:ActionScriptBlittingPart1.as, ActionScriptBlittingPart2.as, ActionScriptBlittingPart3.as, ActionScriptBlittingPart4.as和spritesheets。Spritesheets文件夹中包含了用于 ActionScript示例的PNG文件。5. ? ? ? ?在Package Explorer中,右键点击ActionScriptBlitting项目在弹出菜单上选择Properties。6. ? ? ? ?在Properties属性对话框中点击ActionScript Applications。7. ? ? ? ?点击Add,选择ActionScriptBlittingPart1.as,并点击OK。8. ? ? ? ?将ActionScriptBlittingPart2.as,ActionScriptBlittingPart3.as, 和 ActionScriptBlittingPart4.as重复第7步。9. ? ? ? ?点击OK现在Flash Builder 4中示例代码就设置好了,你就可以运行该示例。
用ActionScript嵌入一个sprite sheet?? ? ? 在ActionScript中你可以通过使用Embed元数据标签嵌入图像。(更多信息请查看Embedding metadata with Flash)一旦它们被嵌入,你可创建类的实例并附加到显示列表,如ActionScriptBlittingPart1.as:
ActionScriptBlittingPart1.as

package{import flash.display.Sprite;[SWF(width=480, height=320, frameRate=24, backgroundColor=0xE2E2E2)]public class ActionScriptBlittingPart1 extends Sprite{ public function ActionScriptBlittingPart1(){addChild(new BrownCollector());}[Embed(source="spritesheets/browncollector.png")]public var BrownCollector:Class;}}
?要运行第一个示例,请参照下列步骤:?? ? ?在Package Explorer中,在ActionScriptBlittingPart1.as文件上点击右键选择Run Application。?? ? ?当浏览器打开时,你会看到所有单元格都有PNG图像(browncollector.png),效果见图1.?? ? ?关闭浏览器窗口。Blitting a sprite sheet?? ? 第二步,你将要用到Flash Player API的Bitmap 和 BitmapData,从sprite sheet复制一个单元格(或帧)到屏幕上,而这个操作可以通过BitmapData.copyPixels()方法来完成,复制输入位图数据的像素到正在制作的位图实例上。在ActionScript中纳入blitting,copyPixels()方法还提供参数来定义要复制输入位图的区域以及如何定义和合并alpha像素。

ActionScriptBlittingPart2.as
package{   import flash.display.Bitmap;   import flash.display.BitmapData;   import flash.display.Sprite;   import flash.geom.Point;   import flash.geom.Rectangle;   [SWF(width=480, height=320, frameRate=24, backgroundColor=0xE2E2E2)]   public class ActionScriptBlittingPart2 extends Sprite   {      public function ActionScriptBlittingPart2()      {         // Create input bitmap instance        spritesheet = (new BrownCollector() as Bitmap).bitmapData;         // Add a Bitmap to the display list that will copyPixels() to.         canvas =  new BitmapData(480, 320, true, 0xFFFFFF);         addChild(new Bitmap(canvas));         rect = new Rectangle(0, 0, 40,40); // 1st Tile         //** Section 1 ** //         // rect = new Rectangle(40, 0, 40, 40); // 2nd Tile         // rect = new Rectangle(80, 0, 40, 40); // 3rd Tile         // ...         // rect = new Rectangle(160, 120, 40, 40); // 20th Tile          canvas.copyPixels(spritesheet, rect,  new Point(0, 0));         //** END Section 1 **/              /** Section 2 ** //         for (var i:int = 0; i < 20; i++)         {            rect.x = (i % 5) * 40;            rect.y = int(i / 5) * 40;            canvas.copyPixels(spritesheet, rect, new Point(i*10, 0));            // Section 3:            // canvas.copyPixels(spritesheet, rect,            // new Point(i*10, 0), null, null, true);         }         //** END Section 2 **/      }        [Embed(source="spritesheets/browncollector.png")]      public var BrownCollector:Class;      public var canvas:BitmapData;       public var spritesheet:BitmapData;      public var rect:Rectangle;   }}
?在Package Explorer中的ActionScriptBlittingPart2.as文件上点击右键并选择Run Application。你将会看到来自sprite sheet的第一个单无格显示在浏览器中(见图2)。


图2 ActionScriptBlittingPart2输出(利用Section 1代码)

显示所有单元格现在你绘制了1个单元格了,为什么不是绘制所有单元格呢?若要绘制所有,参照以下步骤:1. ? ? ? ?在Flash Builder 4中打开ActionScriptBlittingPart2.as2. ? ? ? ?找到标为“Section 1”的代码处并注释掉它。3. ? ? ? ?找到标为“Section 2”的代码处取消注释。4. ? ? ? ?保存文件5. ? ? ? ?再次运行ActionScriptBlittingPart2.asSection 2的代码使用了循环来绘制每格较之前的单元格效果水平偏移10像素。


图3 ActionScriptBlittingPart2输出(利用Section 2代码)?? ?不过,看起来结果并不完全正确,而这正是因为BitmapData.copyPixels()的alpha参数导致的,最后三个参数(alphaBitmapData, alphaPoint, 和 mergeAlpha)提供了不同地处理透明区的方式。由于sprite sheet中的PNG图片已处理过alpha数据,就不再需要alphaBitMapData 或 alphaPoint了。你只需要设置最后一个参数来打开alpha合成,mergeAlpha为true。确保改变:1. ? ? ? ?注释掉Section 2中调用copyPixels()处:
canvas.copyPixels(spritesheet, rect, new Point(i*10, 0));复制代码

2. ? ? ? ?取消注释Section 3中调用copyPixels()处:

?? ? ? ?canvas.copyPixels(spritesheet, rect, new Point(i*10, 0), null, null, true);复制代码

3. ? ? ? ?保存该文件。4. ? ? ? ?再次运行ActionScriptBlittingPart2.as。你会看到图像有序地重叠(见图4)

?
图4 ActionScriptBlittingPart2输出(利用Section 3代码)让sprite sheet动起来?? ? 现在你已知道如何显示来自sprite sheet的位图数据,接下来就是让它动起来。ActionScriptBlittingPart3.as中的代码赋予brown collector图像活力,可将它移动到舞台上鼠标点击处。在Flash Player中利用计时器创建一个平滑的动画?? ? 基本思想是使用一个可以基于Flash Player帧的timer计时器,或两者结合的计时器。一个典型的方法是结合使用ENTER_FRAME事件和调用getTimer()来控制各种电脑环境中动画的速度。下面的代码摘录自ActionScriptBlittingPart3.as(行62):
/*** Handles the timer*/private function enterFrameHandler(event:Event):void{tickPosition = int((getTimer() % 1000) / framePeriod);if (tickLastPosition != tickPosition){tickLastPosition = tickPosition;canvas.lock();canvas.fillRect(canvasClearRect, 0x000000);render();canvas.unlock();}}
?enterFrameHandler()方法处理ENTER_FRAME事件。该代码确定了SWF启动之后经过的秒数,这一数字除以framePeriod,期间游戏设计者想要呈现动画,如果这个值不同于上一次渲染帧的事件,清除画布上的内容,动画被重新渲染。?? ? 在Flash Player中定时(有时描述为elastic race track)可以改变,触发ENTER_FRAME事件可以在不同的机器上有显著地变化。结合这个事件和定时检测可以使动画在一致频率时平滑,即使是一台机器运行速度远比SWF帧频快。同样如果该SWF帧频增加,动画帧频可以维持在一个一致较低的频率,使CPU能够处理其它逻辑而无需渲染每一帧。无论哪种方式,你都需要找出渲染一致性和CPU利用率之间的平衡。如果你把帧频设置过高,低端机器可能无法处理。?? ?当你运行ActionScriptBlittingPart3.as程序时,你会看到一个brown collector围绕着圆圈旋转。Moving the animation?? ?Flash Player MouseEvent.MOUSE_UP 事件提供了鼠标在舞台上点击的x和y的坐标值。将鼠标点击处坐标一直作为collector的当前x和y位置,你可以在每次画布渲染时移动图像到不同的位置处。这给予了用户移动它的能力。下面的代码是从ActionScript类ActionScriptBlittingPart3.as(79行)摘录了如何用blitting来移动动画:
/***    Render any bitmap data.*/private function render():void{          rect.x = (currentTile % 5) * 40;   rect.y = int(currentTile / 5) * 40;   collectorX += (destX-collectorX-20)/5;   collectorY += (destY-collectorY-30)/5;   canvas.copyPixels(spritesheet, rect,   new Point(collectorX, collectorY), null, null, true);   currentTile = ++currentTile % 20;}/***    Used to move the animation around.*/private function mouseUpHandler(event:MouseEvent):void{   destX = event.stageX;   destY = event.stageY;}}
?mouseUpHandler()方法存储了鼠标点击处的x和y坐标值作为目标地址。之后的render()方法确定了一个在collector与目标位置之间非线性运动,记录点击的全局坐标作为新位置,然后将它提供给copyPixels()方法作为blitting的位置。?? ?如果动画运行时在舞台中点击,该collector会朝着点击位置处移动。合并多张sprite sheet动画?? ? 最后示例在一个单独的Stage上整合了多个sprite sheet。在上一个示例中,为了移动动画你需要存储位图数据放置的信息。当合并动画,你将需要保持更多位置的跟踪。除了位置,你可能需要维护并处理深度(它决定了当两个位图重叠时会显示哪一个),变化动画状态,不同的动画帧频,碰撞检测等等。?? ?在ActionScriptBlittingPart4.as中,collector深度最低。随机创建地彩色凝胶从屏幕顶部下降。如果一个凝胶碰撞到collector,将会运行第三种动画——凝胶在collector上熔化。深度?? ?在Flash的中你可能曾创建过动画,如在舞台上放置多个对象。你可能还使用过mx.effects包来移动或旋转对象。如果对象重叠,其z- index(在显示列表中的深度)决定了对象叠放顺序。虽然位块传输,只有一个对象要显示:目标位置。由于正在处理渲染,那么你也将需要保持对象深度跟踪并确保一切都按正确的顺序进行复制。?? ?为了保持彩色凝胶从collector的顶部开始动画,你必须管理blitting的秩序。在ActionScriptBlittingPart4.as 中的render()方法,collector已经被混合(行110)后完成所有凝胶块传输(行138和144)创建凝胶与其元数据?? ?每个彩色凝胶创建于enterFrameHandler()方法中。当一个彩色凝胶被创建,在createGel()方法中会设置其初始化属性包括一个随机x,y为0的位置,默认状态,meltFrame为零,一个唯一名称。该凝胶随后被保存到gels中,render()方法中会遍历这个 Dictionary实例。
/***    Create a gel*/private function createGel():void{   var gel:Object = new Object();   gel.posX = ((Math.random() * 0xffffff) % 280) + 20   gel.posY = 0;   gel.state = "animate";   gel.meltFrame = 0;   gel.name = "gel" + gelCount++;   gels[gel.name] = gel;}
?凝胶的逻辑与渲染状态render()方法移动彩色凝胶的逻辑并更改熔解状态。
/***    Render any bitmap data.*/private function render():void{   rect.x = (currentTile % 5) * 40;   rect.y = int(currentTile / 5) * 40;   collectorX += (destX-collectorX-20)/5;   collectorY += (destY-collectorY-30)/5;   canvas.copyPixels(spritesheetCollector, rect,   new Point(collectorX, collectorY), null, null, true);   // Render Gel at half the frame rate, to slow it down   if (currentTile % 2 == 1)   {      rect.x = ((currentTile-1) % 5) * 40;      rect.y = int((currentTile-1) / 5) * 40;   }   for each (var gel:Object in gels)   {      // Hit Check 5 px within Y and X      if (Math.abs(gel.posY - collectorY + 6) < 14        && Math.abs(gel.posX - collectorX) < 10)      {        gel.state = "melt";      }      if (gel.state == "melt")      {        // Clear out if done melting        if (gel.meltFrame < 20)           gel.meltFrame++;        else        {           delete gels[gel.name];           continue;        }                rect.x = (gel.meltFrame % 5) * 40;        rect.y = int(gel.meltFrame / 5) * 40;        canvas.copyPixels(spritesheetGelMelt, rect, new Point(collectorX-1, collectorY-12), null, null, true);        continue;      }      else      {        canvas.copyPixels(spritesheetGel, rect, new Point(gel.posX, gel.posY), null, null, true);      }           gel.posY += 3;      if (gel.posY > 320)      {        delete gels[gel.name];        continue;      }          }     currentTile = ++currentTile % 20;}
?凝胶动画是collector速度的一半,通过为奇数帧重置rect值为currentTile-1。接下来,在屏幕上代码循环所有彩色凝胶并检测 collector碰撞。如果发生碰撞它会更改状态为“melt”。在这种状态下,render()用彩色凝胶熔解sprite sheet并且其meltFrame总数为20帧时移除。当凝胶与collector的x和y位置发生碰撞时,会出现溶解动画,只要凝胶在移动就会紧随 collector。如果没有发生碰撞,凝胶向下移动3像素,当它的y坐标超出320时将会从舞台上移除。正如你所看到的,利用blitting你必须处理所有动画的逻辑。

读书人网 >编程

热点推荐