原文:http://www.adobe.com/devnet/flash/articles/optimizing-flash-performance.htmlhtml
翻译:http://bbs.9ria.com/thread-156860-1-1.html算法
在这篇文章中,你会学到优化Flash Professional应用性能的策略。优化过程包括编辑你的FLA工程文档确保发布的应用程序帧频能够知足动画的播放流畅。数组
若是你曾运行过一个Flash工程,见过播放老是停顿的动画,固然这种状况你很是不想看到。若是你想来作测验重现这种停顿的动画,能够建立一个有简单动画的工程,而后设置帧频为小于10的任意数字(例如5)。而后发布,能够看到这个动画有多么停顿了。浏览器
有两个主要因素能够决定Flash的性能:CPU或GPU[解释:图形处理器(Graphics Processing Unit) ]的使用和内存的使用。这些因素不是互相独立的。一些优化方法也许在这个方面能够提高性能,可是会对另外一个方面有反作用。在下面的单元里,我会解释他们的工做原理,提供一些让你能够明确的作决定的缘由,好比,为了下降CPU或GPU的加载而增长内存的使用。缓存
若是你是为移动设备开发Flash游戏的,极可能你须要一些下面将要讨论的技术手段来达到可接受的帧频。若是你是开发桌面应用的(非游戏),极可能用很小的帧频就能够达到可接受的效果,或者不熟悉这篇文章里描述的技术也能够。并发
判断和衡量游戏性能框架
在理想的世界里,Flash的测试环境容许你模仿目标平台,而后根据目标平台的状况判断你的应用运行状况。不幸的是,除非你的开发平台和目标平台类似,不然如今还不能评估出在测试环境中你的项目的运行状况。函数
除非,你在开发环境中衡量你的应用性能,而后按期让它在目标平台中运行一下,确认它在目标平台也运行良好。工具
如何你在目标平台测试项目并发现问题,你能够用MT类来调试你的应用来解决问题。(在提供的例子文件文件夹内,打开位于这个目录的AS类:MT/com/kglad/MT.as。)oop
内存追踪,内存使用,和性能测试
MT代码改编自Damian Connolly,能够访问他的网站。这个MT类会打印出帧频、内存消耗,列出内存中存在的对象。为了更好使用MT类,遵循如下步骤:
1.导入MT类:
import com.kglad.MT;
2.在文档类里初始化它,或在项目的主时间轴上这样写:
MT.init(this,reportFrequency);
上面这行代码,“this”表示引用影片的主时间轴,“reportFrequency”表示一个有符号整数(这个数字是本身填的)。主时间轴的引用是用来计算和实现帧频的,reportFrequency是频率(以秒计算),它会跟踪一个Flash应用的帧频输出报告和内存数量的消耗。若是你不想定时输出帧频和内存报告数据,传0(或比0更小的数字)。即便你选择不输出帧频,你仍然运行了这个类的内存跟踪。
3.为了跟踪应用里你建立的对象,加上这句话:
MT.track(whatever_object,any_detail);
上面这行代码的第一个参数是你想跟踪的对象(看看它是否从内存中移除了),第二个参数是可选的字符串,它包含任何你想测试的东西。(有些开发人员会用这个参数获得特定对象是什么,在哪和或者存在的时间等细节。)
4.为了建立报告,显示你跟踪的对象是否还在内存里,加上这句话:
MT.report();
你不必了解MT的代码,只管用就好了。可是,了解一些Dictionary类是如何存储全部传给MT.track()的弱引用也是好的。这个类里包括如何使用它的注释。
在这篇文章的开头提供了许多使用MT类的示例文件测试。为了更多的学习MT类,查看这些测试例子看看MT类是怎么用的。
就像物理里的观察者效应,咱们观察帧频和(或者)内存,和(或者)跟踪内存,改变应用的帧频和内存使用状况。可是,若是观察输出结果比较少极可能观察的效果也会下降。此外,没有绝对的观察数字。每过一段时间调试和优化,改变帧频和/内存使用的状况才是最重要的。MT类很好的作到了承担追踪这些变化的责任。
为了下降由于频繁调用trace方法,而出现虚假的低帧频状况,MT类不容许每秒输出结果。(trace方法自己会下降帧频。)要十分注意这点,若是能够的话,你能够用textfield代替trace方法,来尽量的消除调用trace方法给帧频带来的混淆影响。
在范例文件测试工程里,MT类是惟一的工具来检查内存使用和精确的内存问题。你也能够直接检查CPU或GPU的使用状况(查看执行应用程序时帧频的实际使用状况【就是看资源管理器】)。
实现优化算法
在这个单元,我会开始作一些内存管理的指导,下面单元标题的顺序是按照首字母排序的。而后为了这个目的,我会提供有关CPU/GPU管理信息的子标题来探讨。
也许咱们会以为提供两个单元的技术是合理的。可是,若是你通读完这篇文章,知道了用内存管理影响CPU/GPU的方法,那么列出的内存管理的建议,能够和CPU/GPU单元里列出的方法一块儿使用,这样效果会更好。
在为你提供特定的最佳实现方法以前,我认为技术问题一样重要,知道的多了你就学的轻松,反之就会很累。我一样会列第二个清单,它会按技术获益的优先级次序从高到低排列。
记住这些清单是主观的。它的顺序是依据我的开发经验和能力来定的,还有测试情形和测试环境。
应用的优化技术从易到难排列
1.不要使用滤镜
2.尽量使用倒序for循环,避免使用do循环和while循环
3.明确的中止使用Timer,以便垃圾回收
4.使用弱引用时间侦听器,当不用的时候移除
5.尽量在任什么时候候严格定义变量类型
6.当不须要鼠标交互的时候明确的禁用鼠标交互
7.尽量在任什么时候候使用回调函数来取代dispatchEvent(继承的)类
8.不须要声音时中止Sound类,以便垃圾回收Sound(继承的)类和SoundChannel(继承的)类
9.尽可能让每个所需的元素使用最基本的DisplayObject类
10.Air应用(移动设备)老是使用cacheAsBitmap 和cacheAsBitmapMatrix (前一个是位图缓存,后一个是位图矩阵缓存?我没用过)
11.尽量在任什么时候候从新使用Object
12.Event.ENTER_FRAME循环:使用不一样的侦听器和不一样的侦听函数应用在尽量少的DisplayObjects 上
13.用PoolObject(对象池)取代建立和垃圾回收Object
14.使用局部位图传输(块传输)
15.使用阶段的块传输
16.使用Stage3D
优化技术后好处由大到小排列
1.使用阶段的块传输(若是有足够的系统内存)
2.使用Stage3D
3.使用局部位图传输(块传输)
4.在移动设备上使用cacheAsBitmap 和cacheAsBitmapMatrix
5.当鼠标交互不须要的时候明确的禁用鼠标交互
6.不要使用滤镜
7.须要的时候使用最基本的DisplayObject类
8.尽可能在任什么时候候从新利用对象
9.Event.ENTER_FRAME循环:使用不一样的侦听器和侦听函数,他们最好应用在尽量少的DisplayObjects 上
10.尽量使用倒序for循环,避免使用do循环和while循环
11.用PoolObject(对象池)取代建立和垃圾回收Object
12.尽量在任什么时候候严格定义变量类型
13.使用弱引用时间侦听器,当不用的时候移除
14.尽量在任什么时候候使用回调函数来取代dispatchEvent(继承的)类
15.明确的中止使用Timer,以便垃圾回收
16.不须要声音时中止Sound类,以便垃圾回收Sound(继承的)类和SoundChannel(继承的)类
记住这些优先级排序,而后前进到下个单元,学习如何更新你的Flash工程来更有效率的管理内存。
管理内存
下面列的建议不够详尽,但它包括了那些能够大幅度提高Flash性能的策略内容。
使用回调函数 VS dispatEvent
当派发事件的时候会增长内存的使用,由于每一个事件必须被建立而且分配内存给它。这种行为是这样解释的:事件也是对象,所以也须要内存。
我试着发送少许事件,发现每一个消耗40到128字节。我也发现使用回调函数会使用更少的内存,比使用事件效率更高。(查看在实例文档里的测试文件callback_v_dispatchEvent。)
应用滤镜
当你大量应用滤镜时也很消耗内存。根据Adobe帮助文档,使用一个滤镜会消耗双倍内存。在真实Flash Professional CS6的测试环境中,我曾发现使用滤镜的确会增长内存消耗,可是这种消耗不接近双倍内存。(回顾测试范例,在filters文件夹下)
为每一个元素使用正确类型的现实对象
Shape,Sprite,和MovieClip对象每一个都使用不一样的内存数量。一个Shape对象须要236字节,Sprite须要412字节,MovieClip须要448字节。
若是你在一个工程里使用上千的显示对象,若是不须要交互的话,你也许须要大量Shape类来拯救你的内存。或者,当不须要时间轴时使用Sprite类。
对象池
当你打开你的应用时,要建立各类你会一直使用的对象引用,对象池能够将这些引用保存在数组里。任什么时候候一个对象须要时,就能够从这个数组里取出使用。
不管什么时候当一个对象再也不须要时,把它再从新放回数组里。
有种常规作法是用Vector来代替Array来存储相同类型的对象。使用Vector也许能够比使用Array快两倍,可是!除非你要作成百上千次的操做,不然你不会注意到二者的差异,由于小于上千次的操做它们同样快。(能够看看array_v_vector 文件夹下的范例文件。)
使用对象池能够得到性能上的好处,同时更主要的收益是让管理内存变得简单。若是你在内存利用方面有无限制增加的问题,用对象池能够很好的解决,它是提升性能、下降内存使用的通用技术。
我看到当测试一个每帧包含许多要垃圾回收和再利用的SWF文件里,使用对象池后帧频快了10%,而内存使用则减小了10%。(能够查看pooling_v_gc 文件夹里的范例。)
重用对象
当你要在一个循环里建立许多对象时,最好在循环外先建立一个对象,而后再循环里重复利用它。固然这个方法也不是对全部工程都有效的,但在不少状况下这个技术仍是有用的。
在描述位图传输的单元包括一个重用大量对象的例子。你能够在测试文档里看这是怎么实现的。
处理声音
有关声音的问题在内存使用方面是很是小儿科的。当播放一段声音时,它不可能被垃圾回收的(可使用Flash Professional CS6来测试文件)。当声音播放完或一个SoundChannel实例执行中止声音时,Sound类就准备垃圾回收了。(想学更多的话能够看看名为sound_test 文件夹下的范例。)
使用Timer
使用Timer时要格外当心。若是没有中止Timer(有两种状况:1.currentCount 属性小于它的循环次数;2.没有调用stop()方法),Timer就不会被垃圾回收,即便你已经移除了侦听器,而后将全部引用设为null。一旦你移除了侦听器,Timer的侦听函数就不被再次调用,可是Timer却仍然消耗内存。
Timer类仅仅使用72字节的内存,因此极可能在一个基于桌面/浏览器的Flash游戏里成为一个很不起眼的问题。可是,当你在移动设备里反复的打开、播放、关闭游戏,而后不断重复启动游戏,你也许就看到这个难以忽略的问题了。
看看这个代码,打开命名为gc_timer_test文件夹下的文件。
弱引用侦听器 VS 强引用侦听器
另外一种没法预料的测试结果是,你使用MT类没有办法看出使用弱引用侦听器和强引用侦听器的差异。在Flash Professional CS6环境下个人测试里它们都被当作弱引用侦听器来对待。(查看strong_v_weak_listeners 文件夹下的范例。)
管理CPU/GPU使用状况
当前,我惟一知道如何直接查看的工具就是使用操做系统自带的。Windows里有一个任务管理(性能选项卡)和Mac OS提供的活动监视器。这两个工具均可以让你看CPU的使用状况,可是通常来讲,它们对测试Flash性能不是特别有用。
结果,你直接查看CPU/GPU的使用只能经过检查你应用的帧频了。MT类可让你检查项目的帧频,还有内存使用报告和内存跟踪。
处理cacheAsBitmap 和cacheAsBitmapMatrix
使用DisplayObject的cacheAsBitmap属性能够大幅度提升性能(和内存),只要DisplayObject不经受须要频繁更新位图的改动。换句话说,DisplayObject在某种程度上不改变外观只是改变它在舞台上的位置。若是频繁更新位图,性能会下降。
你能够常常改变位图缓存,仍然能够看到性能上的收益,这取决于几个因素,但不要太惊讶,最重要的因素是,你是如何常常改变位图的。
不管如何,用MT类测试一个指定的工程,而后看看用位图缓存和不用有什么差异。(当决定是否对那些不须要位图改变的显示对象使用位图缓存时要不加思考的就使用!)
若是你有一个显示对象(如影片剪辑),你想使用位图缓存属性,加上这句:
mc.cacheAsBitmap = true;
即便你改变显示对象的大小、倾斜、透明度和或者旋转(但不改变影片剪辑的帧数),而后发布到移动设备,使用位图缓存也是能提高性能的。
尤为是,当把一个工程发布到移动设备时,你能够启用cacheAsBitmap并分配catheAsBitmapMatrix属性,完成后可大幅提高性能,像这样:
mc.cacheAsBitmap = true;
mc.cacheAsBitmapMatrix = new Matrix();
不要使用默认单位矩阵。之后你就会知道有几个缘由促使你使用这个属性而不是用默认矩阵。
Stage blitting(我不知道把它翻译为“阶段块传输”仍是“舞台块传输”)
这是一个描述数据传输的术语,包括了将使用的位图最终渲染到显示屏幕上。不是将显示对象加到显示列表里,而是把像素“放在”舞台大小的位图里,而后把位图“加到”舞台上。为了更新动画,位图的像素要在一个循环里更新。尤为是在Event.ENTER_FRAME循环里,使用BitmapData类里的copyPixel()方法,将舞台大小的位图里的BitmapData属性,在动画的循环以外替换其余的bitmapData对象。
这个方法比直接把对象放到显示列表里复杂,但它更有效率——若是你有个无法容忍的帧频和须要高帧频的Flash应用,这个方法会很是有用。诚然,除非你想增长帧频,不然你绝对没有理由使用这个方法。
我比较了一个SWF文件,它有10,000个正方形影片剪辑,执行运动和旋转动做,还要穿过通过舞台(能够看blit_test/blit_test_mc.fla范例)。而后我把这个文件作了一些基本的优化(能够看blit_test/blit_test_basic_optimizations.fla文件)和stage blitting(看blit_test/blit_test2文件)。
第一个SWF文件大概为15fps,这是不能容忍的。可是,在应用最难的技术优化好比块传输以前,几个基本的调整就能够轻松提升性能。
首先,我将循环倒序,这样有了一点的性能提高(看下面循环的单元),而后,更重要的是,我使用一些常量取代了一些相同的但要重复计算的变量。这些调整时性能有了稍微大点的提高(约40%),让帧频能够稍微让人接受点了,约21fps。
使用stage blitting编码一样的显示区域,结果帧频变成了54fps,整整提高了350%。
可是,正如我以前说的,这个技术的过程很复杂,包括下面几个方面:
1.初始化须要在每一个Event.ENTER_FRAME事件里循环的,要在舞台上显示的位图资源(Bitmap实例,BitmapData实例和Rectangle实例)。
2.建立一个全部要显示更新数据的数组。(这步不是必须的。)
3.建立一个BitmapData对象的数组。若是你的动画在一个影片剪辑的时间轴上,这是你要每帧都存储BitmapData对象的地方(例如,使用一个sprite列表,在范例文件夹里我为每一个角度的矩形都建立了BitmapData实例,这个实例能够用AS旋转。)
4.建立Event.ENTER_FRAME事件循环。
5.更新循环里的元素,将第3步里建立数组里相应的像素,复制到在第1步里建立BitmapData实例对应的地方(第2步决定使用data数组)。
想看更多细节,请看blit_test/blit_test2,它还包括额外的注释。
Stage blitting技术的负面,不是复杂的编码,而是也许在建立须要的位图是消耗大量的内存。当为相似iPad之类有很高分辨率(第1、二代1024*768,第三代2048*1536),相对低的内存(RAM)和容量(1、2、三代分别为256MB,512MB和1GB)的设备写应用时,这是个要严肃考虑的问题。
通常来讲,你的游戏应消耗很少于一半的可用RAM。这指的是,不只包括位图而是你游戏里的一切消耗。
Partial blitting
正如字面含义,局部复制结合了Flash显示列表和把像素复制到BitmapData对象两种方式。特别是,在舞台的每个显示对象是位图时,把他们加入显示列表,而后像通常的显示对象好比影片剪辑那样操控就好了。把每一个对象的动画复制到一个BitmapData对象的数组里。
例如,使用以前有正方形运动选择穿过舞台的文件例子,我把正方形和它们各类旋转,将这些BitmapData对象存放在一个数组里,放在bitmap里加入显示列表,而后在Event.ENTER_FRAME循环里操控这些bitmap就像操控任何显示对象那样(好比以前描述的影片剪辑)。最后,我将bitmap的bitmapData属性分配给对应的数组元素。(看看这是如何实现的,能够复习blit_test/partial_blitting_test.fla文件。)
在个人电脑上,Partial blitting测试(24-26fps)不会像stage blitting同样快。可是这个方法为你启发了思路,由于也许在其余方面partial blitting比stage blitting快。另外,partial blitting比stage blitting好编码。因此呢,若是你用partial blitting技术能够获得效果好的帧频,那么它还能够减小在stage blitting里必需要作的额外工做。(就是若是能用局部复制就能够不用stage blitting了。)
有关Event.ENTER_FRAME 循环
在一个实例上,建立多个Event.ENTER_FRAME侦听器,回调多个函数,要比一个实例上建立一个侦听器回调一个函数,这个函数再调用其余函数,要稍微快那么一点点。(好绕口啊~~~~~~)
可是,这有个不一样的状况:在多个对象上分别侦听Event.ENTER_FRAME,和一个对象上侦听一个相比较,使用一个对象侦听一个是多个对象拥有各自侦听器性能的大约两倍。(能够看enterframe_test_one_v_many_loops_with_different_movieclips 文件夹下的例子。)
理解For循环,while循环和do循环
在Flash里,for的倒序循环是最快执行的循环。若是在循环里须要存储的都是相同类型的对象,一个保存全部对象引用的,使用Vector的倒序for循环是最快的。
若是你使用int而不是uint来迭代元素,那这三个循环都执行的都挺快。若是你递减循环变量而不是增长,那么三个循环也会同样快。(注意:若是你递减的循环变量i使用的停止条件是i>=0,而且i是uint的话,你可能会触发一个没有结束的循环。)
若是你使用的是变量或常量做为循环结束的标志而不是表达式或对象属性,那么三个循环同样快。由于初始条件仅须要评估一次(而不是每次循环迭代都要判断),在任何循环里循环里,使用判断式或对象属性做为初始条件都没有大的差异。
任何不会影响循环的内容都应该放到循环的外面。这包括在循环外定义对象(看重用对象的单元),有时在循环里使用新的构造函数能够放在循环外面,若是结束条件是个表达式,应该在循环外算出来。
我曾看过这种说法,对每个有个下个对象引用的对象循环(相似链表),要比一个数组存储全部对象引用的循环快。在个人测试结果显示,这是不对的。
使用数组比先初始化再使用要快和容易。使用Vector而不是数组,固然要更快了。(见for_loop_v_sequential_loop 文件夹下的例子。)
全部的这些建议可能在不少状况下没什么很大差异。可是,若是你的代码要利用一切能够利用的资源,或者你的工程里有数量惊人的迭代,这些细节值得你参考。
禁用鼠标交互
影片剪辑和sprite能够和鼠标交互。即便你没有为鼠标交互编任何代码,当这些对象存在时Flash Player会检查鼠标交互。因此你能够禁用一些不须要的交互拯救一点CPU资源。
当你注意到性能问题,鼠标滑过舞台时(或者你的电脑风扇加快转速),这个策略很是有用。禁用鼠标交互能够提高性能还可让你的电脑风扇安静点。
在测试时,我看到当禁用全部影片剪辑的鼠标事件后,帧频增长了2 1/2倍,这个测试代码在mouse_interactivity 文件夹下。
移除事件侦听器
即便最新版本的Flash Player出现了两个功能:当对象被垃圾回收后移除侦听器,和强引用侦听器再也不延迟垃圾回收。你仍然要尽量明确地移除全部的事件侦听器。侦听器越是迅速的移除掉,被占用的CPU资源越少。另外,你可能不知道你装了哪一个版本的Flash Player,老版本是没有垃圾回收对象的——即那些对象是弱引用侦听器。不要依赖最新的Flash Player功能,而要踏实优化本身的糟糕代码。
有关Stage3D
Stage3D是基于GPU的显示渲染模型,它是Flash Player11版本发布的。这个模型对3D渲染特别有用,可是使用例如Starling的框架也能服务于2D显示。
由于本来CPU承担了所有的渲染显示工做,(当运行一个程序时CPU也要作其余工做),而如今GPU承担了一部分渲染工做,就可让CPU有更多的空闲作其余工做。这样的利用极大的提升了设备性能。
想看Stage3D内容,你必须使用Flash Player11或更高版本。想看Stage3D的API,你须要用Flash Player11或更高版本发布SWF文件。若是你使用Flash Professional CS6工做,那它已经所有设置好了。若是你用的是Flash Professional CS5或CS5.5,你能够更新Flash安装文件使之能够发布Flash Player11。更多的细节,能够看Rich Galvan写的名为Adding Flash Player 11 support to Flash Professional CS5 and CS5.5博客。
很不幸,使用Stage 3D的API很是困难。可是,有几个免费开源的框架能够生成使用Stage 3D所需的最基本的代码。
其中之一就是Starling,被用来开发2D游戏。它简单易学而且高效简化了Stage3D的复杂性。Starling 的API能够在Starling框架参考网站里寻找。
我测试了Starling来看它和blitting、partial blitting相比较如何。在某些状况下,Starling表现的比这两种blitting要很差。事实上,他表现的比没有优化的10,000个正方形影片剪辑的测试还要糟糕。
可是,在Starling测试里,若是你不选择容许编译选项,那么这小小的改变可使帧频快两倍,输出的SWF文件能够比得上没有优化的10,000个正方形测试。可是这个结果依然让人失望,部分的问题在于,我使用编译版本的Flash Player来测试时,在编译和不编译两种状况下,Starling在编译状况下表现的很差。
另外,10,000个正方形影片剪辑测试不能显示出Starling最好的一面。若是你使用许多包含各自时间轴动画的影片剪辑,Starling会比几乎任何你使用的简单优化技术要出色。
仅仅blitting优化的性能就要优于使用Stage3D和Starling所带来的收益。可是blitting也许不是那么实用,由于内存须要建立所需的位图。
范例文件在starling_test 文件夹下。
使用Starling框架时,要遵循如下步骤:
1.下载Starling.swc文件。
2.使用如下步骤将它导入你的Flash工程连接库:
1)选择,文件>发布设置>脚本设置。
2)点击库路径选项卡,而后点击浏览,选择你下载好的swc文件的位置路径。
3)在“打开文件”对话框,选择你放swc文件的路径。
4)点击“打开”,添加starling.swc到你的连接库路径。
5)点击OK关闭高级ActionScript 3.0设置面板,而后再次点击OK关闭发布设置。
6)保存Fla文件,你就可使用Starling了。
(我通常都是在builder里导入和使用的,估计这里有人看了会迷茫,我翻译的不是太好,本身多选两次就知道了,让我偷个懒吧!)
若是你用Stage3D发布移动air游戏(它包括了相似Starling的框架来使用Stage3D),能够设置渲染模式来指导。若是你发布的是内置HTML文件,可在发布设置里设置窗口模式进行指导。
你能够在Adobe Gaming site学习更多有关Starling和Stage3D API的信息。
下一步
除上面所讨论的优化技术外,开发Flash项目要提升重放性能时,还有另外两种你可使用的最佳实践技术:
1.为你声明的每一个变量肯定类型,若是你肯花时间肯定全部变量的类型,代码会执行的更快,当遇到错误时,编译显示的错误信息也更具描述性和帮助性。能够查看测试范例variables_typed_v_untyped。
2.使用Vector而不是array来存储数据信息。为了看到它的性能,能够看array_v_vector文件。
但愿这篇文章的推荐的大纲能够帮你提升在Flash Professional里建立工程的性能。想要学习更多有关创建Flash动画,应用和游戏的信息,能够访问Flash Developer Center。