高效AS3编码的技巧

3

AS3和许多面向对象编程语言一样,包含许多优化技术,这些技术可能对经验不足的用户来说是未知的。这些技术可以涵盖微观-宏观编码差异和文件结构。以下是值得了解的提示,将丰富AS3程序员的知识。

核心思想:介绍优化方法给新手程序员,他们可能只知道基础知识,但不了解“老练”程序员的各种技巧(尤其是循环中的技巧)。

由于大多数AS3程序员都会使用Flash或Flex,因此与它们有关的编程技巧也自然受到欢迎。

请每个答案限制一个提示,这样最好的提示就会自然浮现到顶部=)

然而:AS3是一种“编译语言”,它本身会进行很多优化,所以对于新手来说,过度遵循这些提示并不是必要的。从中学习,而不是成为其奴隶。第一步始终是“完成应用程序”。


1
听起来像是:“让我们在一页上汇集Flash平台工艺的所有智慧和传统!” - Michael Antipin
14个回答

4
当创建对象时,请注意节俭。在可能的情况下,重复使用杂项对象
更好的说法是:尽可能地重用,但从重复使用杂项对象开始很容易。
这可以为垃圾收集器节省大量工作,从而影响应用程序的整体性能。这样,您还可以节省创建冗余实例所浪费的时间。 Tip 1: 矩阵。
// DO: reusing a matrix.

private static const MATRIX:Matrix = new Matrix();
// <...>
var bmp:BitmapData = new BitmapData(width, height, true, 0);
MATRIX.identity();
MATRIX.scale(0.5, 0.5);
bmp.draw(source, MATRIX);

// DON'T
var bmp:BitmapData = new BitmapData(width, height, true, 0);
var matrix:Matrix = new Matrix();
matrix.scale(0.5, 0.5);
bmp.draw(source, matrix);

提示2:积分。

// DO: reusing a point.

private static const ZERO_POINT:Point = new Point();
// <...>
var thresholdTest:BitmapData = new BitmapData(bmp.width, bmp.height, true, 0);
thresholdTest.threshold(bmp, bmp.rect, ZERO_POINT, ">", 0x10000000, 0xFFFF0000, 0xFF000000);

// DON'T

var thresholdTest:BitmapData = new BitmapData(bmp.width, bmp.height, true, 0);
thresholdTest.threshold(bmp, bmp.rect, new Point(), ">", 0x10000000, 0xFFFF0000, 0xFF000000);

提示3:避免过度使用会为您创建冗余实例的方法,除非这是绝对必要和方便的。或者至少将这些方法的使用减少到应用程序会话期间的单个调用。

  • localToGlobal()globalToLocal():尝试自己计算坐标。否则,每次调用都会创建一个Point实例。
  • getRect()getBounds():查看是否可以以不需要检测复杂位移的方式构造显示列表。否则,每次调用都会创建一个Rectangle实例。
  • ...rest:Array:显然,在每次方法调用时,这会创建一个在方法返回时被处理的数组。这是绝对必要的吗?也许您可以在方法外部创建数组,并在每次重用它(请参见下一个提示)?

提示4:在可能的情况下,当您仅将它们用作生成某些结果的容器的单个ArrayDictionary实例。

// DO: reuse array that holds results

public function generateMeSomeResults(results:Array):void
{
    results.length = 0;
    // <...>
    // fill results array with what you need
    // and reuse this reference next time
}

// DON'T

public function generateMeSomeResults():Array
{
    var results:Array = [];
    // <...>
    return results;
}

小提示5:不要每次想要更改someDisplayObject.filters时都创建新的数组和新的过滤器对象。如果您在动画过程中使用了过滤器,并且它们在每帧中都有变化,则尤其重要。

// DO: reuse filters array and filter objects

private static const EMPTY_FILTERS:Array = [];
private static const GLOW:GlowFilter = new GlowFilter(0xFF0000, 1, 12, 12);
private static const HOVER_FILTERS:Array = [GLOW];

private function onRollOver(event:MouseEvent):void
{
    // it's Ok to change constant here
    // because once filters property is set,
    // you are free to change filter object: it will not affect
    // filters that were set before.
    GLOW.color = 0xFF0000+Math.random()*0xFF00;
    this.filters = HOVER_FILTERS;
}

private function onRollOut(event:MouseEvent):void
{
    this.filters = EMPTY_FILTERS;
}

// DON'T

private function onRollOver(event:MouseEvent):void
{
    this.filters = [new GlowFilter(0xFF0000, 1, 12, 12)];
}

private function onRollOut(event:MouseEvent):void
{
    this.filters = [];
}

还有一件重要的事情需要注意: DisplayObject.filters是一个读写器。当你获取DisplayObject.filters的值时,实际上会创建一个新对象,这并不是你想要的。因此,重用过滤器数组是一个好主意:你一直只有一个实例。

提示6: 对于ColorTransform对象也同样适用Tip 5。每次更改显示对象的色调都不需要单独的ColorTransform实例。一旦设置了DisplayObject.transform.colorTransform = someColorTransform;,就可以自由地更改someColorTransform对象:它不会影响已应用的颜色变换。

就像对待filters一样,该属性也是一个读写器。当你获取DisplayObject.transform.colorTransform的值时,实际上会创建一个新对象,这并不是你想要的。参见下面的测试。

import flash.geom.ColorTransform;

var test1:ColorTransform = transform.colorTransform;
var test2:ColorTransform = transform.colorTransform;
trace(test1 == test2); // always false. this object gets created each time on access.

如果可能的话 =)请将5个提示分开,包括重用提示XD - PicoCreator
2
所有这些技巧都展示了杂项对象的重复使用。 - Michael Antipin
AS3 对象看起来很昂贵 :p - gosukiwi

4
如果你对游戏编程很认真:进化你的层次结构,不要使用深层次结构,学习实体/组件架构(聚合/组合与继承),深入研究PushButton引擎源代码。

4

位图传输对于Flash游戏尤其是移动端至关重要。 - Chunky Chunk

3

使用[]和new Object()来替代new Array()和{},这样做速度最快可达3倍之多。在循环中操作更加昂贵时,这种方法就更加常用了。

一般来说,使用new关键字就意味着性能要降低。

编辑:更新内容如下:出人意料的是,最新版本的Flash大大提高了“new Object();”语句的性能。这似乎适用于CS5及以上版本。因为我在CS4中进行的测试结果与此相反。[{}比new Object()更好]

无论如何,想要亲眼看到效果的人可以自行尝试。

import flash.utils.getTimer;

public var _time:Number;
public var _simpleArrayTime:Number;
public var buffer:Array;
public function testArray():void
{
    trace( "----------------Array Test--------------");
    _time = getTimer();
    for ( var a:int = 0; a < 100000; a++ )
        buffer = [];
    _simpleArrayTime = getTimer() - _time;
    trace( "[] * 100000 :"+_simpleArrayTime.toPrecision(21) );

    _time = getTimer();
    for ( var b:int = 0; b < 100000; b++ )
        buffer = new Array();
    _simpleArrayTime = getTimer() - _time;
    trace( "new Array() * 100000 :"+_simpleArrayTime.toPrecision(21) );

    _time = getTimer();
    for ( var c:int = 0; c < 100000; c++ )
        buffer = [];
    _simpleArrayTime = getTimer() - _time;
    trace( "[] * 100000 :"+_simpleArrayTime.toPrecision(21) );
}

public var objBuffer:Object;
public function testObject():void
{
    trace( "----------------Object Test--------------");
    _time = getTimer();
    for ( var a:int = 0; a < 100000; a++ )
        objBuffer = {};
    _simpleArrayTime = getTimer() - _time;
    trace( "{} * 100000 :"+_simpleArrayTime.toPrecision(21) );

    _time = getTimer();
    for ( var b:int = 0; b < 100000; b++ )
        objBuffer = new Object();
    _simpleArrayTime = getTimer() - _time;
    trace( "new Object() * 100000 :"+_simpleArrayTime.toPrecision(21) );

    _time = getTimer();
    for ( var c:int = 0; c < 100000; c++ )
        objBuffer = {};
    _simpleArrayTime = getTimer() - _time;
    trace( "{} * 100000 :"+_simpleArrayTime.toPrecision(21) );
}

public function runTests(event:Event = null):void
{
    testArray();
    testObject();
}

以下是我的结果。
----------------Array Test--------------
[] * 100000 :82.0000000000000000000
new Array() * 100000 :152.000000000000000000
[] * 100000 :53.0000000000000000000
----------------Object Test--------------
{} * 100000 :53.0000000000000000000
new Object() * 100000 :36.0000000000000000000
{} * 100000 :53.0000000000000000000

2
你需要证明那个说法。例如,在我的测试中,我发现你关于Array是正确的。差别大约是3倍。但是,同样的情况并不适用于Object。事实上,{}new Object()要昂贵1.6倍。这与你所建议的相反。我需要研究一下为什么会这样,但是你是在暗示[]{}没有发生new吗?如果是这样,那么正在发生什么? - Brian Genisio
等一下,什么? 它怎么能快 3 倍?虽然是相同的东西但语法不同。如果这是真的,那将非常有趣且值得了解,但你能提供这方面的来源吗? - Chunky Chunk
@Brian Genisio,嗯...惊讶的是,似乎CS5确实使new Object()更好了。我已经更新了问题以反映时代的变化(哈哈)...而且,我认为新语句通常是昂贵的。不仅适用于数组或对象=) - PicoCreator
@TheDarkIn1978 加入了一个测试代码,让你自己亲眼看看,哈哈。不过似乎我对对象已经过时了(更新了答案)。 - PicoCreator
@Yordan Yanakiev 你可能需要重新审视一下语句和测试脚本。=) - PicoCreator
显示剩余3条评论

3

当您需要在一个屏幕上显示多个图像副本时,请勿创建多个BitmapData:使用单个BitmapData实例,只需将其引用传递给单独的Bitmap对象即可。

import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.Bitmap;

// create one image
var bmp:BitmapData = new BitmapData(100, 100, true, 0);
bmp.perlinNoise(36, 36, 3, 100, false, false, BitmapDataChannel.BLUE|BitmapDataChannel.GREEN);

// *** display the same image multiple times ***

var test1:Bitmap = new Bitmap(bmp);
test1.x = 0;
test1.y = 0;
addChild(test1);

var test2:Bitmap = new Bitmap(bmp);
test2.x = 30;
test2.y = 30;
addChild(test2);

var test3:Bitmap = new Bitmap(bmp);
test3.x = 60;
test3.y = 60;
addChild(test3);

当您加载外部资源库(SWF)并通过applicationDomain.getDefinition()new获取BitmapData项时,缓存所获取的内容可能会非常有用。


说到BitmapData,还有一个特定的dispose()方法用于BitmapData对象,在将Bitmap/BitmapData对象置空/清理之前应该调用它。 - Chunky Chunk
但是你必须非常确定你的应用程序中没有任何地方需要这个 BitmapData 实例。如果它被多个 Bitmap 实例显示,调用 dispose() 将会销毁所有正在显示的可视内容。 - Michael Antipin

3

尽可能使用向量而非数组。与数组相比,它可以提高约60%的性能。

向量是具有可变类型和/或数组长度限制的数组。

http://www.mikechambers.com/blog/2008/08/19/using-vectors-in-actionscript-3-and-flash-player-10/

一般来说,不要费心优化所有内容,只有在需要优化时才进行优化。如果你优化了占性能95%的代码的3%,那么你已经实现了类似于优化100%代码所花费时间的三分之一或33倍的整体时间的性能...


1
类型是关键,虽然数组并不实际暗示其内容,但选择合适的向量类对代码阅读会有很大帮助。 :) - Yordan Yanakiev

2

学习自己使用位运算。在主渲染循环、位图或音频处理中使用它,并学会在不需要时不使用它以保持程序员友好的代码(这是困难的部分)。


或者,你可以使用它,但是在上面加一个注释,包含非位运算版本。这样就可以以可读的方式阅读了。=) - PicoCreator

2

1
如果你正在使用Flex框架编程,请务必记住Flex组件生命周期并在构建自己的组件时加以利用。这里有关于MX生命周期Spark生命周期的信息。
生命周期利用Flash Player的渲染事件,通过将类似的更改分组到代码中的单个位置来节省时间和性能。

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接