2012年3月28日 星期三

[AS3] 取得FUNCTION 的名稱

其實我只是在想一個可以偷懶得好方法~
這樣在 LOG檔可以不用寫太多東西~

來簡介一下~
目前我會的取得FUNCTION NAME的名稱的方法


public function getFunctionName(f:Function, root:*):String
{
var t:Object = root;
var methods:XMLList = describeType(t)..method.@name;
for each (var m:String in methods)
if (t.hasOwnProperty(m) && t[m] != null && t[m] === f) return m;          
return null;
}
而且還要配合
trace(getFunctionName(arguments.callee, this));

這樣子來使用!
非常麻煩 而且還有限定 是公開 非靜態的方法

雖然很早就在 Ticore 大的BLOG 上拜見過這招
不過我都一直沒想到可以拿來應用!

直到我看到某個範例

public static function getName():String {
var error:Error = new Error();
var stackTrace:String = error.getStackTrace();     // entire stack trace
if(stackTrace == null) return 'null';
var startIndex:int = stackTrace.indexOf("at ", stackTrace.indexOf("at ") + 1); //start of second line
var endIndex:int = stackTrace.indexOf("()", startIndex);   // end of function name
var lastLine:String = stackTrace.substring(startIndex + 3, endIndex);
var functionSeperatorIndex:int = lastLine.indexOf('/');
var functionName:String = lastLine.substring(functionSeperatorIndex + 1, lastLine.length);
return functionName;
}

這招 BUG 到逆天了
不管是靜態 公開 私用 等等的FUNCTION
通通都可以抓的到
更扯的是可以抓到上一層 上上一層
雖然還是有使用限制!
在非 debug 或是 ADL的環境下無法使用!
因為 getStackTrace 會是 null
不過跟原來的那招比~
好用很多了

更扯的是!


getFunctionName函數執行1000次 消耗的時間為2057毫秒
getName函數執行1000次 消耗的時間為45毫秒
不過直接輸出字串 約 1毫秒....

這兩個執行時間的差距!
如果你是跟我一樣的偷懶開發者!
在LOG的部分 不要忘了這招! 而且在非 debug的播放器下自動失效!

下面是我測的是文件

public function testLocalClassCall()
{
unitTestTool.addFunction('test02', this);
unitTestTool.addFunction('test03', this);
unitTestTool.addFunction('test04', this);
unitTestTool.startFunTest(1000);
unitTestTool.start();
}

public function test02():void
{
var s:String = getFunctionName(arguments.callee, this);
}

public function test03():void
{
var s:String = getName();
}

public function test04():void
{
var s:String = 'test04';
}

下面是他的輸出!(每次輸出都有差!)

[SWF] C:\FlexWorks\testLocalClassCall\bin-debug\testLocalClassCall.swf - 6,432 bytes after decompression
test02函數執行1000次 消耗的時間為1843毫秒
test03函數執行1000次 消耗的時間為43毫秒
test04函數執行1000次 消耗的時間為1毫秒
[Unload SWF] C:\FlexWorks\testLocalClassCall\bin-debug\testLocalClassCall.swf

2012年3月8日 星期四

[AS3] 調教效能問題:濾鏡 並不會比較耗資源!

一樣也是在公司遇到的一個小狀況
讓我 非常非常的訝異~

多層濾鏡所構成的元件在 the miner 監測下所耗用記憶體
和 一張 PNG 所構成的元件 是一樣的!

本來我推論 可能會在 CPU 的耗用上有差異
但是更嚇的人是~
沒有~兩個我自己在測的時候都是 10% 左右~
圖中所見的差異...
那應該抓圖時間的錯置的問題...

大家可以自己回去測看看~




這個是我自己弄得一個小測試~~
個人懷疑多層濾鏡的元件
自動的轉成類似 bitmapdata的方式(我沒勾點陣快取 也沒設定)
而不是和以前一樣試即時運算的結果~
所以比較不吃資源和記憶體!


所以期待靠把濾鏡去掉 去增進效能的部分
最好還想想就好~
就我目前
做的一些部分來分享一下心得



1. 
把滑鼠事件關閉 能省的資源不多 
特別是物件不多的情況下  
層次不多 或是 根本沒監聽的情況下
關閉 是省不了太多資源的!

甚至如果你是一層一層一個一個元件關 那消耗的資源還會比較多 !
以我自己的例子 一個mc 物件下面有 200~300個 sprite原件~
你沒關 所消耗的資源量 在沒監聽的情況下
基本上 和只關 最上面 mc 元件是一樣的
如果你跟我一樣傻傻的從最裡面的 SP 元件開始關起!
那恭喜 你還會消耗更多的資源

但是我用很多 我自己覺得蠻 BUG的寫法~
利用事件流的部分~所以關閉對我自己的案子來說
是有作用的

2.
把濾鏡 換成 圖片(就這篇文章專為這點寫的)
說真的用處真的不大~
不過換成圖 還是會有一點點的縮減的
特別是你用換的圖會比較小 而且重複利用的時候
個人懷疑 他是把每個元件都快取成 點陣圖
但是每個點陣圖 是獨立存在記憶體中的!
所以換成同一個元件! 會有一點點的幫助!

3.
減少使用 movieClip 真的會節省很多資源!
特別是內有複雜原件的 mc 拆成多個 簡單的 sprite 在某種程度上
是非常有用處的
個人在這個部分 節省了相當 相當多的記憶體
第一點提到的 mc 原來內部都是用 mc 去包!
但是我只是將 底層類別改成 sprite
整體來說 減少了 20~30 mb的記憶體的資源
比我換了 400~500張左右的圖
所節省的 20mb 左右還來的多.

4.
大圖換小圖 有用!
多張靜態合成一張 有用!

5.
剩下的就是一些
縮減無用的程式碼
物件變數清空
固定不用的變數改成常數
減少使用一些過度的變數

6.
還有 到MC 的最後一格
如果你用程式讓他回到第一格!
在某些情況下 不會構成 物件 消失在建造的情況
如果你沒下 那這個物件會在更新一次
雖然不知道這部分 會影響多少~
但介意的人 就多一個CODE巴

7.
其實 timer 或是 setTimeout 等的東西
吃記憶體也是蠻凶的~個人懷疑 某開場爆增的 20mb
就是因為這個部分
不過這部分我自己是還夾了 事件發送..
等等等的東西在~
所以也不太確定!

有再考慮把這部分換成 enterFrame 去監聽 !
看看會不會比較省一點~
雖然我覺得不會~不過至少就某種程度來說
好控制~
比寫在元件最後一個影格送出EVENT 或是 seTimeout 的部分都好控制多了

雖然這幾點 感覺用處不大!
但是還是有一點點的用處~
有類似問題的可以考慮試看看

以我手上的案子~
最一開始沒調整過
開場約是 144~150 mb左右的記憶體
經過我自己一堆調整後
開場約 118 ~124
不過 穩定後 大約都是  106~118 左右的記憶體!

如果以關閉全部動畫和元件中的事件流...
穩定的時候 大約可以壓到 89mb左右....(最佳情況)

不過遊戲中 有一個算是特殊遊戲的東西~
這部分很誇張~
載入就約 220左右~
效能最高可以吃到 699mb

經過我一些調整後 大約最高 650mb
不過開場始終壓不下來~慘的是還會飆的比沒調高....
讓我自己覺得非常的失敗~
而且還是將大圖換小圖後的結果!

不過穩定約 186 mb 都是差不多的! 調前調後都一樣
不過我已經將大量 可以換成 靜態圖 取代的 濾鏡效果都替換了
連 大量的 大圖我都 換成小圖了....

這邊我想除了我從 程式的部分去調外~
原件的部分 我真的不知道怎樣調了...

[AS3] 調用本地的CLASS

調用 外部SWF 的CLASS 已經是稀鬆平常的事了~
調用本地類別 的CLASS 通常都是直接打 比較快 還有提示~
萬一本地的類別太多 須要靠拼字來調用
會讓程式碼減短一點
可以利用這招
this.loaderInfo.applicationDomain.getDefinition('className')
來調用~
這招我用很久了~
不過到最近工作才發現~
原來他有一個限制

就是一定要新增到場景上后 才能調用該類別 !

下面是測試的程式碼

package
{
import flash.display.Sprite;

public class testLocalClassCall extends Sprite
{
public function testLocalClassCall()
{
trace("test !!!!");
var cla:Class;
if(this.loaderInfo.applicationDomain.hasDefinition('mc1'))
cla  = this.loaderInfo.applicationDomain.getDefinition('mc1') as Class;
trace('mc1 ' + cla)
}
}
}


理論上 會有這樣的輸出

test !!!!
mc1 : null
原來以前某些案子的 null 是這樣來的
所以以後調用 本地端的函數
還是先寫好 switch 然後~
靠拼字去取得 class 比較妥當