2011年11月12日 星期六

一起來玩鳥 Starling Framework(2)效能測試以及Image與Texture

上一篇我們放了一個Quad與TextField在舞台上慢慢轉。眼尖的可能會發現轉起來邊緣有點鋸齒,這可以透過設定Starling的反鋸齒來解決,在Main.as裡,新增了_starling之後,可以加上
_starling.antiAliasing = 2;
後面的值可以是0到16間2的次方數(0, 1, 2, 4, 8, 16),預設為0(沒有反鋸齒效果),通常有需要的話也不會設大於2。設其他數值也可以跑,不過應該是自動取了最接近值。這一篇,我們就來測測Starling的效能有多強,另外介紹最基本的圖像Image與Texture。

我們來做個簡單的測試:在舞台上加入大量的物件,這裡先使用最簡單的Quad,然後隨時間不斷變換這些Quad的位置與角度,觀察大約可以加多少物件後還能維持高的fps。Main.as與上個範例一樣,就不重複介紹,只要把Starling的rootClass換成Game1就好。之後的練習大概也是這樣。我們先把Game1的程式碼列出來:
public class Game1 extends Sprite 
{
  private var _container:Sprite;
  private var _quads:Vector.<Quad>;//存放所有的Quad
  private const QUAD_NUM:int = 800;//Quad個數,調整這個數值來測試效能

  public function Game1()
  {
    super();
    addEventListener(Event.ADDED_TO_STAGE, init);
  }

  private function init(e:Event):void
  {
    removeEventListener(Event.ADDED_TO_STAGE, init);
 
    _container = new Sprite();
    addChild(_container);
    addChild(new Stats());
    _quads = new Vector.<Quad>();
    var quad:Quad;
    for (var i:int = 0; i < QUAD_NUM; i++)
    {
      quad = new Quad(20, 20, uint(Math.random() * uint.MAX_VALUE));//長寬20x20,顏色亂數指定
      quad.x = Math.random() * stage.stageWidth;//亂數產生座標
      quad.y = Math.random() * stage.stageHeight;//亂數產生座標
      quad.rotation = deg2rad(Math.random() * 360);//亂數旋轉
      _container.addChild(quad);//加到場景上
      _quads.push(quad);//存放到Vector裡,方便等等變換位置角度
    }

    addEventListener(Event.ENTER_FRAME, onEnterFrame);//每次進入影格就改變所有Quad的位置與角度
    stage.addEventListener(ResizeEvent.RESIZE, onResize);//stage監聽ResizeEvent.RESIZE,舞台大小改變時要改變Starling stage的大小與viewPort範圍
  }

  private function onEnterFrame(e:Event):void
  {
    //改變所有Quad的大小與rotation
    var quad:Quad;
    for (var i:int = 0; i < QUAD_NUM; i++)
    {
      quad = _quads[i];
      quad.x = Math.random() * stage.stageWidth;
      quad.y = Math.random() * stage.stageHeight;
      quad.rotation = deg2rad(Math.random() * 360);
    }
  }

  private function onResize(e:ResizeEvent):void 
  {
    stage.stageWidth = e.width;//必須手動改變stage的大小
    stage.stageHeight = e.height;//必須手動改變stage的大小
    Starling.current.viewPort = new Rectangle(0, 0, e.width, e.height);//改變viewPort的範圍
  }
}
首先我們加了800個Quad到場景上,隨時間隨意變換位置與rotation。deg2rad是starling.utils裡的靜態方法,用來將角度轉為弧度,先反的也有rad2deg可以把弧度轉為角度。為了觀察舞台大小對效能的影響,我們對stage加上監聽舞台大小變化的事件。這裡比較不一樣的,第一,事件類型為ResizeEvent.RESIZE,這是因為ResizeEvent才有width跟height告訴我們現在flash的stage大小是多少。第二,我們必須依據這width與height來改變Starling stage的stageWidth與stageHeight,然後要改變Starling的viewPort。要取得現在正在運行的Starling的參照,可使用Starling.current。如果只改變stage的大小,Starling裡的元件會變形,render的範圍不變;如果改變viewPort大小,則元件會變形,render範圍會改變。只有同時改變兩個,元件才不會變形,同時render範圍隨著舞台大小而改變。

測試的結果,Quad數量與viewPort大小都會影響效能。在我電腦上測,1920x1080的螢幕(實際舞台高度會小一點),大概800個Quad還可以維持在5x fps,偶而會掉到3x~4x。
點我或圖Demo瘋狂的Quad

接著我們來介紹Image與Texture。這兩者的分別有點像native裡的Bitmap與BitmapData。Image是DisplayObject,是顯示圖片最基本的一個類別;Texture是貼圖的資料。同一個Texture可以給多個Image使用。當DisplayObject的dispose()方法被呼叫時,會釋放該物件佔用的資源,但不會連帶dispose相關的Texture,因為Texture還可能繼續被使用。我們開一個新的rootClass叫Game2,來重覆剛剛的測試,只不過我們把Quad改成Image,並且embed一張圖片來當Texture使用。程式碼先列出來:
public class Game2 extends Sprite 
{
  private var _container:Sprite;
  private var _objs:Vector.<Image>;//改成Image的Vector
  [Embed(source = "/assets/head.png")]
  private static const HeadBitmap:Class;//embed一張圖進來,等等轉成Texture來使用
  private const OBJ_NUM:int = 800;//改一下常數名稱,但基本上還是用來指定物件的數量
  
  public function Game2()
  {
    super();
    addEventListener(Event.ADDED_TO_STAGE, init);
  }
  
  private function init(e:Event):void
  {
    removeEventListener(Event.ADDED_TO_STAGE, init);
   
    _container = new Sprite();
    addChild(_container);
    addChild(new Stats());
    _objs = new Vector.<Image>();
    var texture:Texture = Texture.fromBitmap(new HeadBitmap());//新增一個Bitmap,再用Texture.fromBitmap來轉成Texture
    var image:Image;
    for (var i:int = 0; i < OBJ_NUM; i++)
    {
      image = new Image(texture);//新增Image,指定剛剛的材質給它
      image.x = Math.random() * stage.stageWidth;//亂數產生座標
      image.y = Math.random() * stage.stageHeight;//亂數產生座標
      image.width = image.height = 32;//指定image寬高
      _container.addChild(image);//加到場景上
      _objs.push(image);//存放到Vector裡
    }
   
    addEventListener(Event.ENTER_FRAME, onEnterFrame);
    stage.addEventListener(ResizeEvent.RESIZE, onResize);
}
首先embed一張png來使用。也可以用Loader讀取進來再轉成Bitmap。產生Texture有幾個方法:從Bitmap,BitmapData,ATF(Adobe Texture Format,一種快要開放出來的Texture格式),以及其它的texture。文件裡可以找到對應的方法。我們embed或load一張圖進來是一個Bitmap,自然是採用fromBitmap()最快。產生一個Texture,代表upload一個貼圖的資料到GPU,等著需要的時候使用。Starling的Texture長寬最好是2的次方數,長寬可以不同。原因是之前Sparrow是運行在手機上,而手機上的OpenGL ES2有這樣的限制。將來Starling支援到手機之後勢必也會有這個限制,因此Starling會自動幫我們把Texture的長寬轉成2的次方數,而這樣會影響一點點效能。以上是印象中從Tutorial Book裡看到的。另外,texture有個mipMapping屬性,預設為true,代表Starling會幫我們產生一些材質縮圖,這樣小物件貼圖時會比較好看,關掉會節省一些效能,但物件縮小時貼圖應該會比較醜。
有了texture,在新增Image時第一個參數傳進去,就可以了。沒特別設定的話就是texture的大小。Image有個smoothing屬性,是個字串,可使用TextureSmoothing.BILINEAR(中等效果),TextureSmoothing.NONE(沒有平滑化),TextureSmoothing.TRILINEAR(最高效果),預設為TextureSmoothing.BILINEAR。加完Image之後,其他程式碼大同小異,就不再多提了。跑出來效能跟Quad差不多,也是800個可以順暢的運行。
點我或圖Demo瘋狂的Image

最後,看了一下CPU的使用率,基本上它是上上下下,不太穩定的,從個位數到20出頭。當fps順一點時,CPU使用率就高一點;當fps降一點時,CPU使用率也跟著降。或者應該反過來說才對?
上一篇:一起來玩鳥 Starling Framework(1)一定要的Hello World!
下一篇:一起來玩鳥 Starling Framework(3)Button!

沒有留言:

張貼留言