查看文章 |
许多类型的游戏,用户体验都依赖于终端可拥有的屏幕像素和移动物体有多快。当让大量的DisplayObject对象动起来时,如MovieClip或Sprite对象,Adobe Flash Player可能在表现上会大大折扣。Flash Player必须遍历显示对象树并为每个基于向量的DisplayObject计算渲染输出,这样会消耗CPU周期成为真正的瓶颈,尤其是低端机。 许多屏幕动画游戏都是预先载入一张位图,这种技术被称为blitting(块传输)。块传输虽不能解决所有性能问题,但它能使动画运行平滑,统一大多数机器上动画帧频。Blitting术语来自于 在本文中,我描述了软件位块传输技术并提供示例代码,因此你可以在ActionScript中应用它。 要求为了满足本文条件,你需要下列软件和文件。 Flash Builder 4 betaFlash Player 9 以上版本示例文件:
预备知识本文假定你已熟悉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)。
图1 sprite sheet示例显示了20个单元格,每格为40*40像素 设置 ActionScript 项目在你运行示例代码前,你将需要在Flash Builder 4按照以下步骤设置项目。
现在Flash Builder 4中示例代码就设置好了,你就可以运行该示例。 用ActionScript嵌入一个sprite sheet在ActionScript中你可以通过使用 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;
}
}
要运行第一个示例,请参照下列步骤:
Blitting a sprite sheet第二步,你将要用到Flash Player API的 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个单元格了,为什么不是绘制所有单元格呢?若要绘制所有,参照以下步骤:
Section 2的代码使用了
图3 ActionScriptBlittingPart2输出(利用Section 2代码) 不过,看起来结果并不完全正确,而这正是因为 确保改变:
让 sprite sheet 动起来现在你已知道如何显示来自sprite sheet的位图数据,接下来就是让它动起来。ActionScriptBlittingPart3.as中的代码赋予brown collector图像活力,可将它移动到舞台上鼠标点击处。 在Flash Player中利用计时器创建一个平滑的动画基本思想是使用一个可以基于Flash Player帧的timer计时器,或两者结合的计时器。一个典型的方法是结合使用 /** * 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(); } }
在Flash Player中定时(有时描述为elastic race track)可以改变,触发 当你运行ActionScriptBlittingPart3.as程序时,你会看到一个brown collector围绕着圆圈旋转。 Moving the animationFlash Player /** * 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; } }
如果动画运行时在舞台中点击,该collector会朝着点击位置处移动。 合并多张sprite sheet动画最后示例在一个单独的Stage上整合了多个sprite sheet。在上一个示例中,为了移动动画你需要存储位图数据放置的信息。当合并动画,你将需要保持更多位置的跟踪。除了位置,你可能需要维护并处理深度(它决定了当两个位图重叠时会显示哪一个),变化动画状态,不同的动画帧频,碰撞检测等等。 在ActionScriptBlittingPart4.as中,collector深度最低。随机创建地彩色凝胶从屏幕顶部下降。如果一个凝胶碰撞到 collector,将会运行第三种动画——凝胶在collector上熔化。 深度在Flash的中你可能曾创建过动画,如在舞台上放置多个对象。你可能还使用过mx.effects包来移动或旋转对象。如果对象重叠,其z- index(在显示列表中的深度)决定了对象叠放顺序。虽然位块传输,只有一个对象要显示:目标位置。由于正在处理渲染,那么你也将需要保持对象深度跟踪并确保一切都按正确的顺序进行复制。 为了保持彩色凝胶从collector的顶部开始动画,你必须管理blitting的秩序。在ActionScriptBlittingPart4.as 中的 创建凝胶与其元数据每个彩色凝胶创建于 /** * 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 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速度的一半,通过为奇数帧重置 延伸阅读在本文中你已学会如何创建一个软blitting引擎。你可以使用这些技术在其它Flash Player中渲染情景。你可能想要探索位块传输过程中创建源位图数据的不同方法,例如,转换DisplayObject动画帧到一个位图中并缓存它们到一个数组中。 |





