flash性能优化方案整理(最全)

第1节:代码执行基本原理

LayaAir引擎支持AS3、TypeScript、JavaScript三种语言开发,然而无论是采用哪种开发语言,最终执行的都是JavaScript代码。所有看到的画面都是通过引擎绘制出来的,更新频率取决于开发者指定的FPS,例如指定帧频率为60FPS,则运行时每个帧的执行时间为六十分之一秒,所以帧速越高,视觉上感觉越流畅,60帧是满帧。

由于实际运行环境是在浏览器中,因此性能还取决于JavaScript解释器的效率,指定的FPS帧速在低性能解释器中可能不会达到,所以这部分不是开发者能够决定的,开发者能作的是尽可能通过优化,在低端设备或低性能浏览器中,提升FPS帧速。

LayaAir引擎在每帧都会重绘,在性能优化时,除了关注每帧执行逻辑代码带来的CPU消耗,还需要注意每帧调用绘图指令的数量以及GPU的纹理提交次数。

预处理资源和数据

对于一些处理起来较为麻烦的资源和数据,可以进行预处理,以节约CPU资源。

1.避免使用滤镜,包括经过 Pixel Bender 处理的滤镜。
可能使用通过创作工具(例如 Adobe®
Photoshop®)创建的位图来模拟滤镜。避免在 ActionScript
中使用运行时创建的动态位图。使用外部创作的位图可帮助运行时减少 CPU 或
GPU
负载,特别是当滤镜属性不随时间更改时。如果可能,在创作工具中创建位图所需的任何效果。然后,可以在运行时中显示该位图,而无需对它进行任何处理,这样
速度要快得多。

 

2.mip 映射:尽量少用 mip
映射。尽管它可以改进缩小位图的品质,但它对带宽、内存和速度都有影响。在某些情况下,最好选择使用通过外部工具预缩放的位图版本,并将其导入到您的应用程序中。如果只需缩小位图,不要一开始就使用较大位图。

 

3.避免复杂矢量图:使用矢量(而不是位图)是节省内存的好方法。然而,使用矢量(特别是大量矢量),会显著增加对
CPU 或 GPU
资源的需求。使用位图是优化呈现的一个好方法,因为运行时在屏幕上绘制像素比呈现矢量内容需要的处理资源要少。如果矢量图过于复杂,最好是预处理成位图。

 

4.使用常量来代替反复重新计算相同的值

 

1.3 检查TypeScript编译环境版本

HTML5作为新兴领域越来越热。然而在移动设备硬件性能弱于PC的背景下,对性能的需求显得更为重要,而HTML5性能优化前与优化后有着极大的差别,如何优化才能提高性能,对此熟知的人很少。本文以LayaAir引擎为例,通过代码示例详细阐述如何利用引擎对HTML5作出性能的极致优化。

最大程度减小 CPU 使用量

1.冻结和解冻对象:
要优化您的代码,请始终冻结和解冻您的对象。冻结和解冻对所有对象都很重要,但对显示对象尤其重要。即使显示对象不再位于显示列表中并正在等待作为垃圾回
收,其代码仍然占用大量 CPU。例如,它们仍然在使用
Event.ENTER_FRAME。因此,使用Event.REMOVED_FROM_STAGE和Event.ADDED_TO_STAGE事件正确
冻结和解冻对象非常关键。

 

2.激活和停用事件:有两个事件(Event.ACTIVATE和Event.DEACTIVATE)
可以帮助您微调应用程序以使其尽量使用最少的 CPU
周期。这些事件允许您检测运行时获得或失去焦点的时间。因此可以对代码进行优化,以便对上下文更改做出反应。

 

3.禁用鼠标交互: 使用交互式对象(例如 MovieClip 或 Sprite
对象)时,运行时执行本机代码以检测和处理鼠标交互。当屏幕上显示许多交互式对象时,特别是当它们重叠时,检测鼠标交互可能会占用大量
CPU
资源。避免此处理的一种简便方法是对不需要任何鼠标交互的对象禁用鼠标交互。可以用mouseEnabled和mouseChildren来禁用鼠标交
互。

 

<h3>其中:</h3>
1
laya.core是核心包,封装了显示对象渲染,事件,时间管理,时间轴动画,缓动,消息交互,socket,本地存储,鼠标触摸,声音,加载,颜色滤镜,位图字体等。
2 laya.webgl
包封装了webgl渲染管线,如果使用webgl渲染,可以在初始化时调用Laya.init(1000,800,laya.webgl.WebGL);
3 laya.ani.js是动画模块,包含了swf动画,骨骼动画等。
4 laya.filter.js包含更多webgl滤镜,比如外发光,阴影,模糊以及更多。
5 laya.html.js封装了html动态排版功能。
6 laya.ui.js提供了制作UI的各种组件实现。
7laya.tilemap.js提供tileMap解析支持。

  • 代码执行基本原理
  • 基准测试
  • 内存优化
  • 图形渲染性能
  • 减少CPU使用量
  • 其他优化策略

释放内存

在 Flash Player
的发行版中无法直接启动垃圾回收器,而是被系统自动启动。想要释放一个对象,需要通过删除对象引用的方式进行。Flashplayer
debug版本提供了System.gc()接口,我们可以在程序的创作阶段利用这个函数来检验程序中的对象引用有没有被正确解除。

要正确的释放对象,需要注意如下几点:

1.必须删除对象所有的引用。

2.对象内部成员必须不存在对外部对象的引用。即删除所有内部成员的引用。

3.必须删除EnterFrame,Timer等事件监听器。对于Timer还必须先stop。

4.对于BitmapData,还需要先用dispose()方法删除内存中的像素。

5.对于加载的SWF等,需要先用loader.unloadAndStop()卸载。

6.播放完声音或一个 SoundChannel
实例被用于对声音执行 stop() 时,就已经已准备好对该声音进行垃圾回收。

 

关于弱引用


种引用不会被垃圾收集器作为判定object是否被回收的依据。它的作用是:如果当一个对象仅仅剩下弱引用时,这个对象将会被垃圾收集器在下一轮回收。但
是弱引用只支持两种类型:第一种是经常会因为内存管理机制带来麻烦的事件监听器,建议每当添加监听器时,都将其第五个参数选项,即弱引用设置为true。
下面是其对应的参数设置的例子:

    someObj.addEventListener("eventName",listenerFunction,useCapture,priority,weakReference);
    stage.addEventListener(Event.CLICK,handleClick,false,0,true);
    // the reference back to handleClick (and this object) will be weak. 

另一个弱引用支持的是Dictionary
object。一般情况下在初始化时设置其第一个参数为true,下面是例子:

    var dict:Dictionary = new Dictionary(true);
    dict[myObj] = myOtherObj;
    // the reference to myObj is weak, the reference to myOtherObj is strong 

 

注意:如果系统认为可用内存不是足够低,垃圾回收器可能不会运行。垃圾回收的执行时间不可预知。内存分配(而不是对象删除)会触发垃圾回收。

关于垃圾回收的详细讲解,可以参看Flash AS3
垃圾回收机制详解。

 

tsc -v

主题包括:

优化循环

**1.避免使用for in循环:for in
循环比普通的for循环浪费更多的时间,应该避免。**

 

2.采用更快的算法,尽量用Vector或Array的API.

 

3.尽量优化循环内的代码

有很多蚊子肉,在少量计算时无关紧要,但是在循环中一点的优化说不定也能起到大的作用,这些一点的优化包括:

a.用乘法来代替除法,用位运算代替除2或乘2

b.将多行赋值转为单行赋值(a=b=c=0)

c.将可以在循环外进行的运算提取到循环外。获取某些对象的属性,数组的成员等等都可以算作计算

d.尽可能使用内联代码以减少代码中函数的调用次数。

e.避免使用嵌套的if结构:例如if(a>1&&b>1){}比嵌套的if(a>1){if(b>1){}}要快且代码清晰。

f.避免使用with语句。

g.用int不用Math.floor():前者比后者快。

h.var obj:Object = new Object()比var obj:Object = {}快。

i.var arr:Array = []比 var arr:Array = new Array()快。

j.if (myObj) 比if (myObj != null) 快。

k.当程序里有多条分支时,将频繁发生的分支放在上面,如:

if ( conditionmostpopular )  {   



}  else if ( conditionSomtimes )   {      



}  else   {       // 最少发生



}

 

 

参考:

在命令行输入“tsc -v” 命令可查看当前TypeScript编译的版本

第2节:基准测试

LayaAir引擎内置的性能统计工具可用于基准测试,实时检测当前性能。开发者可以使用laya.utils.Stat类,通过Stat.show()
显示统计面板。具体编写代码如下例所示:

Stat.show(0,0); //AS3的面板调用写法       
Laya.Stat.show(0,0); //TS与JS的面板调用写法

Canvas渲染的统计信息:

图片 1

WebGL渲染的统计信息:

图片 2

统计参数的意义:

FPS:

每秒呈现的帧数(数字越高越好)。
使用canvas渲染时,描述字段显示为FPS(Canvas),使用WebGL渲染时,描述字段显示为FPS(WebGL)。

Sprite:

渲染节点数量(数字越低越好)。
Sprite统计所有渲染节点(包括容器),这个数字的大小会影响引擎节点遍历,数据组织和渲染的次数。

DrawCall:

DrawCall在canvas和WebGL渲染下代表不同的意义(越少越好)。
Canvas下表示每帧的绘制次数,包括图片、文字、矢量图。尽量限制在100之下。
WebGL下表示渲染提交批次,每次准备数据并通知GPU渲染绘制的过程称为1次DrawCall,在每1次DrawCall中除了在通知GPU的渲染上比较耗时之外,切换材质与shader也是非常耗时的操作。
DrawCall的次数是决定性能的重要指标,尽量限制在100之下。

Canvas:

三个数值 —— 每帧重绘的画布数量 / 缓存类型为“normal”类型的画布数量 /
缓存类型为“bitmap”类型的画布数量”。
CurMem:仅限WebGL渲染,表示内存与显存占用(越低越好)。
Shader:仅限WebGL渲染,表示每帧Shader提交次数。

无论是Canvas模式还是WebGL模式,我们都需要重点关注DrawCall,Sprite,Canvas这三个参数,然后针对性地进行优化。(参见“图形渲染性能”)

把耗费CPU的计算分成小而独立的段

 

参见:

Flash平台是一个单线程的平台,它不支持多线程。它运行在一个循环中,以划分的“帧”为单位进行处理。帧的执行又可划分为2个阶段:

(1)ActionScript 代码的执行

(2)显示对象(DisplayObject)的呈现

如果代码执行的时间过长,超出了给定的timeout时限,那么Flash
Player会被锁定或者停止执行代码,如:

[Fault] exception, information=Error: Error #1502:
脚本的执行时间已经超过了 15 秒的默认超时设置。

这时,我们需要将代码分为小而独立的段来进行执行。如下例

var allowedTime = 1000/fps - renderTime - otherScriptsTime;
var startTime = 0;
var savedIndex = 0;
function enterFrame() {
    startTime = getTimer();

    var i, n = array.length;
    for (i=savedIndex; i<n; i++){

        if (getTimer() - startTime > allowedTime){
            savedIndex = i;
            return;
        }

        process(array[i]);
    }

    complete();
}

 

1

第4节:图形渲染性能

优化Sprite

1.尽量减少不必要的层次嵌套,减少Sprite数量。
2.非可见区域的对象尽量从显示列表移除或者设置visible=false。
3.对于容器内有大量静态内容或者不经常变化的内容(比如按钮),可以对整个容器设置cacheAs属性,能大量减少Sprite的数量,显著提高性能。如果有动态内容,最好和静态内容分开,以便只缓存静态内容。
4.Panel内,会针对panel区域外的直接子对象(子对象的子对象判断不了)进行不渲染处理,超出panel区域的子对象是不产生消耗的。

优化DrawCall

1.对复杂静态内容设置cacheAs,能大量减少DrawCall,使用好cacheAs是游戏优化的关键。
2.尽量保证同图集的图片渲染顺序是挨着的,如果不同图集交叉渲染,会增加DrawCall数量。
3.尽量保证同一个面板中的所有资源用一个图集,这样能减少提交批次。

优化Canvas

在对Canvas优化时,我们需要注意,在以下场合不要使用cacheAs:

1.对象非常简单,比如一个字或者一个图片,设置cacheAs=bitmap不但不提高性能,反而会损失性能。
2.容器内有经常变化的内容,比如容器内有一个动画或者倒计时,如果再对这个容器设置cacheAs=bitmap,会损失性能。

可以通过查看Canvas统计信息的第一个值,判断是否一直在刷新Canvas缓存。

关于cacheAs

设置cacheAs可将显示对象缓存为静态图像,当cacheAs时,子对象发生变化,会自动重新缓存,同时也可以手动调用reCache方法更新缓存。
建议把不经常变化的复杂内容,缓存为静态图像,能极大提高渲染性能,cacheAs有”none”,”normal”和”bitmap”三个值可选。

  1. 默认为”none”,不做任何缓存。
    2.当值为”normal”时,canvas下进行画布缓存,webgl模式下进行命令缓存。
    3.当值为”bitmap”时,canvas下进行依然是画布缓存,webGL模式下使用renderTarget缓存。这里需要注意的是,webGL下renderTarget缓存模式有2048大小限制,超出2048会额外增加内存开销。另外,不断重绘时开销也比较大,但是会减少drawcall,渲染性能最高。
    webGL下命令缓存模式只会减少节点遍历及命令组织,不会减少drawcall,性能中等。

设置cacheAs后,还可以设置staticCache=true以阻止自动更新缓存,同时可以手动调用reCache方法更新缓存。

cacheAs主要通过两方面提升性能。一是减少节点遍历和顶点计算;二是减少drawCall。善用cacheAs将是引擎优化性能的利器。

下例绘制10000个文本:

Laya.init(550, 400, Laya.WebGL);
Laya.Stat.show();

var textBox = new Laya.Sprite();

var text;
for (var i = 0; i < 10000; i++)
{
    text = new Laya.Text();
    text.text = (Math.random() * 100).toFixed(0);
    text.color = "#CCCCCC";

    text.x = Math.random() * 550;
    text.y = Math.random() * 400;

    textBox.addChild(text);
}

Laya.stage.addChild(textBox);

下面是笔者电脑上的运行时截图,FPS稳定于52上下。

图片 3

当我们对文字所在的容器设置为cacheAs之后,如下面的例子所示,性能获得较大的提升,FPS达到到了60帧。

// …省略其他代码… var textBox = new Laya.Sprite();
textBox.cacheAs = "bitmap"; // …省略其他代码…

图片 4

文字描边

在运行时,设置了描边的文本比没有描边的文本多调用一次绘图指令。此时,文本对CPU的使用量和文本的数量成正比。因此,尽量使用替代方案来完成同样的需求。

对于几乎不变动的文本内容,可以使用cacheAs降低性能消耗,参见“图形渲染性能
– 关于cacheAs”。

对于内容经常变动,但是使用的字符数量较少的文本域,可以选择使用位图字体。

跳过文本排版,直接渲染

大多数情况下,很多文本都不需要复杂的排版,仅仅简单地显示一行字。为了迎合这一需求,Text提供的名为changeText的方法可以直接跳过排版。

var text = new Text();
text.text = "text";
Laya.stage.addChild(text);
//后面只是更新文字内容,使用changeText能提高性能
text.changeText("text changed.");

Text.changeText会直接修改绘图指令中该文本绘制的最后一条指令,这种前面的绘图指令依旧存在的行为会导致changeText只使用于以下情况:

文本始终只有一行。

文本的样式始终不变(颜色、粗细、斜体、对齐等等)。

即使如此,实际编程中依旧会经常使用到这样的需要。

优化呈现性能

1.减小重绘区域:重绘是Flash
Player性能消耗的主要大户,所以减小重绘区域面积,减少不必要的重绘次数,能减少不必要的性能消耗。可以使用函数
flash.profiler.showRedrawRegions(true);在构建项目时显示重绘区域以减小重绘区域。要减小重绘区域,主要需要做
到如下几点:

不要通过将DisplayObject放在后面来隐藏,虽然这样在界面上看不到了,但是还是会消耗系统资源,应该停止运行隐藏的DisplayObject并将隐藏运行的DisplayObject的visible属性设置为false或将其从显示列表中完全删除。

 

2.在影片品质和效率之间折中。

 

3.避免使用alpha属性:alpha属性会带来不必要的计算,应该避免。

 

4.位图缓存
可以利用AS3自带的cacheAsBitmap属性来设置缓存,这时,应该将opaqueBackground属性设置为特定颜色,运行时会将显示对象
视为不透明,且为DisplayObject设置缓存,而不对容器设置缓存,这样能最大程度的发挥位图缓存的作用。另外可以利用BitmapData自己
实现位图缓存,这样更能有效的提高性能。

 

5.使用Stage3D: 可以使用Stage3D利用GPU绘图。

 

1.2 配置TypeScript编译环境
1 安装node.js
2步骤二:安装完Node.js后,接着就可以使用NPM来安装TypeScript
Compiler,之后就能通过这个Compiler来将TypeScript编译成为JavaScript。在这个步骤里,开发者需要打开cmd命令行工具,输入指令“npm
install -g typescript”,按回车键后,即可开始进行TypeScript
Compiler的安装。
http://ldc.layabox.com/uploadfile/image/20161227/1482829631609586.png
步骤三:如下图所示,当我们看到 “– typescrip@ 版本号”
的时候,可以确认完成了 TypeScript
Compiler的安装,此时,关闭命令行工具即可

第5节:减少CPU使用量

减少动态属性查找

JavaScript中任何对象都是动态的,你可以任意地添加属性。然而,在大量的属性里查找某属性可能很耗时。如果需要频繁使用某个属性值,可以使用局部变量来保存它:

function foo()
{
    var prop = target.prop;
    // 使用prop
    process1(prop);
    process2(prop);
    process3(prop);
}

计时器

LayaAir提供两种计时器循环来执行代码块。

  1. Laya.timer.frameLoop执行频率依赖于帧频率,可通过Stat.FPS查看当前帧频。
  2. Laya.timer.loop执行频率依赖于参数指定时间。

当一个对象的生命周期结束时,记得清除其内部的Timer:

Laya.timer.frameLoop(1, this, animateFrameRateBased);
Laya.stage.on("click", this, dispose);
function dispose() 
{
    Laya.timer.clear(this, animateFrameRateBased);
}

获取显示对象边界的做法

在相对布局中,很经常需要正确地获取显示对象的边界。获取显示对象的边界也有多种做法,而其间差异很有必要知道。

1.使用getBounds/ getGraphicBounds。、

var sp = new Sprite();
sp.graphics.drawRect(0, 0, 100, 100, "#FF0000");
var bounds = sp.getGraphicBounds();
Laya.stage.addChild(sp);

getBounds可以满足多数多数需求,但由于其需要计算边界,不适合频繁调用。

2.设置容器的autoSize为true。

var sp = new Sprite();
sp.autoSize = true;
sp.graphics.drawRect(0, 0, 100, 100, "#FF0000");
Laya.stage.addChild(sp);

上述代码可以在运行时正确获取宽高。autoSize在获取宽高并且显示列表的状态发生改变时会重新计算(autoSize通过getBoudns计算宽高)。所以对拥有大量子对象的容器应用autoSize是不可取的。如果设置了size,autoSize将不起效。

使用loadImage后获取宽高:

var sp = new Sprite();
sp.loadImage("res/apes/monkey2.png", 0, 0, 0, 0, Handler.create(this, function()
{
    console.log(sp.width, sp.height);
}));
Laya.stage.addChild(sp);

loadImage在加载完成的回调函数触发之后才可以正确获取宽高。

3.直接调用size设置:

Laya.loader.load("res/apes/monkey2.png", Handler.create(this, function()
{
    var texture = Laya.loader.getRes("res/apes/monkey2.png");
    var sp = new Sprite();
    sp.graphics.drawTexture(texture, 0, 0);
    sp.size(texture.width, texture.height);
    Laya.stage.addChild(sp);
}));

使用Graphics.drawTexture并不会自动设置容器的宽高,但是可以使用Texture的宽高赋予容器。毋庸置疑,这是最高效的方式。

注:getGraphicsBounds用于获取矢量绘图宽高。

根据活动状态改变帧频

帧频有三种模式,Stage.FRAME_SLOW维持FPS在30;Stage.FRAME_FAST维持FPS在60;Stage.FRAME_MOUSE则选择性维持FPS在30或60帧。

有时并不需要让游戏以60FPS的速率执行,因为30FPS已经能够满足多数情况下人类视觉的响应,但是鼠标交互时,30FPS可能会造成画面的不连贯,于是Stage.FRAME_MOUSE应运而生。

下例展示以Stage.FRAME_SLOW的帧率,在画布上移动鼠标,使圆球跟随鼠标移动:

Laya.init(Browser.width, Browser.height);
Stat.show();
Laya.stage.frameRate = Stage.FRAME_SLOW;

var sp = new Sprite();
sp.graphics.drawCircle(0, 0, 20, "#990000");
Laya.stage.addChild(sp);

Laya.stage.on(Event.MOUSE_MOVE, this, function()
{
    sp.pos(Laya.stage.mouseX, Laya.stage.mouseY);
});

图片 5

此时FPS显示30,并且在鼠标移动时,可以感觉到圆球位置的更新不连贯。设置Stage.frameRate为Stage.FRAME_MOUSE:

Laya.stage.frameRate = Stage.FRAME_MOUSE;

图片 6

此时在鼠标移动后FPS会显示60,并且画面流畅度提升。在鼠标静止2秒不动后,FPS又会恢复到30帧。

使用callLater

callLater使代码块延迟至本帧渲染前执行。如果当前的操作频繁改变某对象的状态,此时可以考虑使用callLater,以减少重复计算。

考虑一个图形,对它设置任何改变外观的属性都将导致图形重绘:

var rotation = 0,
    scale = 1,
    position = 0;

function setRotation(value)
{
    this.rotation = value;
    update();
}

function setScale(value)
{
    this.scale = value;
    update();
}

function setPosition(value)
{
    this.position = value;
    update();
}

function update()
{
    console.log('rotation: ' + this.rotation + 'tscale: ' + this.scale + 'tposition: ' + position);
}

调用以下代码更改状态:

setRotation(90); setScale(2); setPosition(30);

控制台的打印结果是

rotation: 90 scale: 1 position: 0
rotation: 90 scale: 2 position: 0
rotation: 90 scale: 2 position: 30

update被调用了三次,并且最后的结果是正确的,但是前面两次调用都是不需要的。

尝试将三处update改为:

Laya.timer.callLater(this, update);

此时,update只会调用一次,并且是我们想要的结果。

图片/图集加载

在完成图片/图集的加载之后,引擎就会开始处理图片资源。如果加载的是一张图集,会处理每张子图片。如果一次性处理大量的图片,这个过程可能会造成长时间的卡顿。

在游戏的资源加载中,可以将资源按照关卡、场景等分类加载。在同一时间处理的图片越少,当时的游戏响应速度也会更快。在资源使用完成后,也可以予以卸载,释放内存。

事件模型

1.用简单的回调代替事件模型
使用本机事件模型与使用传统的回调函数相比,速度更慢且占用的内存更多。必须创建
Event 对象并为其分配内存,而这会降低性能。例如,当侦听
Event.ENTER_FRAME
事件时,将在各个帧上为事件处理函数创建一个新事件对象。在捕获和冒泡阶段(如果显示列表很复杂,此成本会很高),显示对象的性能可能会特别低。

 

2.减少事件处理函数:利用事件捕获和冒泡可以在一个对象(而不是多个对象)上注册事件处理函数以提高性能。

 

LayaAir引擎库功能
LayaAir引擎支持精灵、矢量图、文本、富文本、位图字体、动画、骨骼、音频与视频、滤镜、事件、加载、缓动、时间、网络、UI系统、物理系统、TiledMap、prtocol等API;支持开发2D、3D、VR的产品研发,支持Canvas与WebGL模式,支持同时发布为HTML5、Flash、APP(IOS、安卓)多种版本。

第6节:其他优化策略

1.减少粒子使用数量,在移动平台Canvas模式下,尽量不用粒子;

2.在Canvas模式下,尽量减少旋转,缩放,alpha等属性的使用,这些属性会对性能产生消耗。(在WebGL模式可以使用);

3.不要在timeloop里面创建对象及复杂计算;

4.尽量减少对容器的autoSize的使用,减少getBounds()的使用,因为这些调用会产生较多计算;

5.尽量少用try catch的使用,被try catch的函数执行会变得非常慢;

重用对象

尽可能重复使用对象并避免重新创建对象,
因为实例化对象成本很高。而且因为内存分配有时会触发垃圾回收,所以重用对象还可以减少垃圾回收器运行的机会,从而提高应用程序运行速度。

1.避免在一个循环中创建对象,尽力将对象创建在循环外部并在循环内反复重用它。如下例,我们就重用了BitmapData对象:

// Create a single 20 x 20 pixel bitmap, non-transparent 
var myImage:BitmapData = new BitmapData(20,20,false,0xF0D062); 
var myContainer:Bitmap; 
const MAX_NUM:int = 300; 

for (var i:int = 0; i< MAX_NUM; i++) 
{ 
    // Create a container referencing the BitmapData instance 
    myContainer = new Bitmap(myImage); 

    // Add it to the display list 
    addChild(myContainer); 

    // Place each container 
    myContainer.x = (myContainer.width + 8) * Math.round(i % 20); 
    myContainer.y = (myContainer.height + 8) * int(i / 20); 
}

 

2.使用对象池,在初始化应用程序期间创建一定数量的对象并将其存储在一个池中,在需要时取用,用完放回池中。

 

第3节:内存优化

对象池

对象池,涉及到不断重复使用对象。在初始化应用程序期间创建一定数量的对象并将其存储在一个池中。对一个对象完成操作后,将该对象放回到池中,在需要新对象时可以对其进行检索。
由于实例化对象成本很高,使用对象池重用对象可减少实例化对象的需求。还可以减少垃圾回收器运行的机会,从而提高程序的运行速度。

以下代码演示使用

Laya.utils.Pool:

ar SPRITE_SIGN = 'spriteSign';
var sprites = [];
function initialize()
{
    for (var i = 0; i < 1000; i++)
    {
        var sp = Pool.getItemByClass(SPRITE_SIGN, Sprite)
        sprites.push(sp);
        Laya.stage.addChild(sp);
    }
}
initialize();

在initialize中创建大小为1000的对象池。

以下代码在当单击鼠标时,将删除显示列表中的所有显示对象,并在以后的其他任务中重复使用这些对象:

Laya.stage.on("click", this, function()
{
    var sp;
    for(var i = 0, len = sprites.length; i < len; i++)
    {
        sp = sprites.pop();
        Pool.recover(SPRITE_SIGN, sp);
        Laya.stage.removeChild(sp);
    }
});

调用Pool.recover后,指定的对象会被回收至池内。

使用Handler.create

在开发过程中,会经常使用Handler来完成异步回调。Handler.create使用了内置对象池管理,因此在使用Handler对象时应使用Handler.create来创建回调处理器。以下代码使用Handler.create创建加载的回调处理器:

Laya.loader.load(urls, Handler.create(this, onAssetLoaded));

在上面的代码中,回调被执行后Handler将会被对象池收回。此时,考虑如下代码会发生什么事:

Laya.loader.load(urls, Handler.create(this, onAssetLoaded), Handler.create(this, onLoading));

在上面的代码中,使用Handler.create返回的处理器处理progress事件。此时的回调执行一次之后就被对象池回收,于是progress事件只触发了一次,此时需要将四个名为once的参数设置为false:

Laya.loader.load(urls, Handler.create(this, onAssetLoaded), Handler.create(this, onLoading, null, false));

释放内存

JavaScript运行时无法启动垃圾回收器。要确保一个对象能够被回收,请删除对该对象的所有引用。Sprite提供的destory会帮助设置内部引用为null。

例如,以下代码确保对象能够被作为垃圾回收:

var sp = new Sprite();
sp.destroy();

当对象设置为null,不会立即将其从内存中删除。只有系统认为内存足够低时,垃圾回收器才会运行。内存分配(而不是对象删除)会触发垃圾回收。

垃圾回收期间可能占用大量CPU并影响性能。通过重用对象,尝试限制使用垃圾回收。此外,尽可能将引用设置为null,以便垃圾回收器用较少时间来查找对象。有时(比如两个对象相互引用),无法同时设置两个引用为null,垃圾回收器将扫描无法被访问到的对象,并将其清除,这会比引用计数更消耗性能。

资源卸载

游戏运行时总会加载许多资源,这些资源在使用完成后应及时卸载,否则一直残留在内存中。

下例演示加载资源后对比资源卸载前和卸载后的资源状态:

var assets = [];
assets.push("res/apes/monkey0.png");
assets.push("res/apes/monkey1.png");
assets.push("res/apes/monkey2.png");
assets.push("res/apes/monkey3.png");

Laya.loader.load(assets, Handler.create(this, onAssetsLoaded));

function onAssetsLoaded()
{
    for(var i = 0, len = assets.length; i < len; ++i)
    {
        var asset = assets[i];
        console.log(Laya.loader.getRes(asset));
        Laya.loader.clearRes(asset);
        console.log(Laya.loader.getRes(asset));
    }
}

关于滤镜、遮罩

尝试尽量减少使用滤镜效果。将滤镜(BlurFilter和GlowFilter)应用于显示对象时,运行时将在内存中创建两张位图。其中每个位图的大小与显示对象相同。将第一个位图创建为显示对象的栅格化版本,然后用于生成应用滤镜的另一个位图:

图片 7

应用滤镜时内存中的两个位图

当修改滤镜的某个属性或者显示对象时,内存中的两个位图都将更新以创建生成的位图,这两个位图可能会占用大量内存。此外,此过程涉及CPU计算,动态更新时将会降低性能(参见“图形渲染性能
– 关于cacheAs)。

ColorFiter在Canvas渲染下需要计算每个像素点,而在WebGL下的GPU消耗可以忽略不计。

最佳的做法是,尽可能使用图像创作工具创建的位图来模拟滤镜。避免在运行时中创建动态位图,可以帮助减少CPU或GPU负载。特别是一张应用了滤镜并且不会在修改的图像。

性能优化的原则

1.避免过早优化:太早优化将使代码难以设计和维护,最好是针对程序的瓶颈进行优化。

2.改进性能有时需要权衡:不能一味注重改进性能,一个项目要考虑各方面的利弊,比如代码的健壮性,结构性,扩展性,可读性,执行效率等等。

 

使用适当的对象

 不同的对象占用的内存不同,而且不同的对象,适用的情况也不同,这时,应尽量选用占内存最小的适当对象类型。

1.选用适当的基本对象:比如int和Number,分别消耗4个和8 个字节. int
类表示的值的范围是:-2,147,483,648 (-2^31) 到 2,147,483,647
(2^31-1),所以如果你的取值范围在这个范围内,最好用int而不是用Number。

 

2.选用适当的显示对象:对于非交互式简单形状,请使用 Shape
对象。对于不需要时间轴的交互式对象,请使用 Sprite
对象。对于使用时间轴的动画,请使用 MovieClip
对象。更多具体情况,参见使用合适的显示子类。

 

3.选择合适的文本对象:对于只读文本,最好使用 Flash
文本引擎,它占用较少的内存并提供更好的呈现效果。对于输入文本,最好使用
TextField 对象,因为在创建典型行为(例如,输入处理和自动换行)时需要的
ActionScript 代码较少。

 

4.尽可能使用 Vector 类而不是 Array 类:Vector 类的读写访问速度比
Array
类快。但是Vector内只能存放同一类型的数据,所以我们要根据需求来使用Vector或是Array。在使用Vector时,注意要为矢量分配特定长
度并将其长度设为固定值,因为如果不提前指定矢量的大小,则矢量大小将在矢量用完空间后增加。每次矢量大小增加时,都将分配一个新的内存块。矢量的当前内
容会复制到新内存块中。这种额外的分配和复制数据会降低性能。尽可能尝试使用
Vector 对象 API,因为它们的运行速度可能更快。

 

5.尽可能使用 String
类方法(如indexOf()、substr()或substring())代替正则表达式来实现基本字符串查找和提取:
在使用正则表达式时如果需要对元素进行分组,但不需要在结果中隔离该组的内容,请使用非捕获组(“(?:xxxx)”)代替正则表达式中的(“(xxxx)”)组。

 

优化网络交互

1.缓存资源:加载资源后将本地缓存,而不是在每次需要时从网络进行加载。

 

2.使用 Flash Remoting 和 AMF 实现优化的客户端-服务器数据通信

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注