WP7 - XNA載入圖片策略與效能
遊戲的圖片載入是最重要的功能,遊戲要玩的順暢,如何讓載入的速度變快也是一門學問,
在學習做2D遊戲的時候,上網搜尋遊戲素材,會發現很多素材都是如下圖
會將很多動作或是一堆物件做在同一張圖片上,而不是個別單一圖檔,
這不是為了網路下載素材方便才這樣做的,
而是因為遊戲載入受到硬體限制,所以希望載入的次數越少越好,
才會將相關的動作或是物品全都集合成一張圖,然後只要載入一次大圖就可以了。
雖然聽起來很合理,不過身為專業的阿宅工程師,不可以道聽塗說、人云亦云,
因此我們就來做做實驗吧!以下都是HD7實機測試。
首先產生100張200X200的png圖,
再用此100張圖拼成一張200X200的大圖,載入程式碼如下:
1: protected override void LoadContent() {
2: // Create a new SpriteBatch, which can be used to draw textures.
3: spriteBatch = new SpriteBatch(GraphicsDevice);
4:
5: Start();
6: LoadLarge();
7: End("LoadLarge cost");
8:
9: Start();
10: LoadSmallList();
11: End("LoadSmallList cost");
12:
13: }
14:
15: private void LoadLarge() {
16: Large = Content.Load<Texture2D>("Large");
17: }
18:
19: private void LoadSmallList() {
20: for (int i = 0; i < 100; i++) {
21: SmallList[i] = Content.Load<Texture2D>(string.Format("Small ({0})", i + 1));
22: }
23: }
LoadLarge就是載入一張大圖,LoadSmallList就是載入100張小圖
執行六次得到的數據如下:
一 | 二 | 三 | 四 | 五 | 六 | 平均 | |
Large | 193 | 182 | 195 | 227 | 249 | 228 | 212 |
SmallList | 446 | 439 | 461 | 441 | 462 | 440 | 451 |
接著我們將兩個載入方式互換,先載入100張小圖,再載入一張大圖,得到如下數據
一 | 二 | 三 | 四 | 五 | 六 | 平均 | |
Large | 182 | 181 | 170 | 184 | 183 | 181 | 180 |
SmallList | 533 | 473 | 452 | 472 | 472 | 473 | 494 |
單位都是毫秒。
由此可知第一次載入會多花費一些時間,但是一張大圖的效率也比多張小圖好上2.1到2.7倍。
不過這是在所有小圖都可以放入大圖內的情形,但是效能差距還是挺明顯的。
知道載入次數對效能影響,再來試試看載入怎樣的大圖會最快,
由於手機載入圖片有最大2048X2048的限制,
所以我們測試的圖片大小分別為1000X1000、2000X500 以及 500X2000,
這三張圖的面積都一樣,理論上是可以塞一樣多的東西,數據如下:
一 | 二 | 三 | 單個檔案大小 | |
1000x1000 | 44 | 44 | 41 | 192K |
2000x500 | 44 | 44 | 43 | 111K |
500x2000 | 43 | 43 | 40 | 305K |
測了三次就不想測了,結果是都一樣。
由上一個測試可以發現第一次載入都會有特別的時間消耗,
因此這次的測試在載入這三個圖之前有先載入一個小圖,
之後這三個圖的載入時間才會如此接近。
由上面第一個實驗可以知道載入速度看的是載入的次數,
而第二個實驗三個檔案大小不一樣,載入速度卻是一樣的,難道是看檔案面積?
其實載入的確除了看次數,另外一個就是看檔案大小,只是檔案大小不是看PNG的檔案,而是看XNA編譯後的XNB檔,
看了一下圖檔的大小,原本只有192K的圖檔,編譯成XNB檔後竟然有3.81MB,這是發生甚麼事??
XNA為了載入快速,會捨棄需要編碼的圖檔格式(如Jpg,Png等),全都轉成不用解碼可以直接讀取的檔案,
因此1000x1000的檔案大小就變成1000*1000*4(bytes) 大約等於3.81MB,而載入的速度就取決於XNB的大小。
甚麼?你說身為專業的阿宅工程師,不可以沒做實驗就認為XNB真的比較快??
那就來簡單做個實驗,關鍵程式碼如下:
1: protected override void LoadContent() {
2: // Create a new SpriteBatch, which can be used to draw textures.
3: spriteBatch = new SpriteBatch(GraphicsDevice);
4:
5:
6: Start();
7: Small = Content.Load<Texture2D>("Small");
8: End("Small cost");
9:
10: Start();
11: Large = Content.Load<Texture2D>("Large");
12: End("L cost");
13:
14: Start();
15: Large2 = Texture2D.FromStream(GraphicsDevice, TitleContainer.OpenStream("Large2.png"));
16: End("L2 cost");
17: }
18:
我們要從專案裡直接讀取檔案的話,要把檔案放到主專案(不是Content專案),
並且Build Action設定成None或是Content,
Copy to Output Directory設定為Copy always。
之後在程式裡可以用TitleContainer取得Stream,再藉由Texture2D.FromStream讀取進來,
數據如下:
一 | 二 | 三 | 四 | 五 | 六 | 平均 | |
xnb | 166 | 162 | 153 | 162 | 163 | 163 | 161.5 |
png | 964 | 920 | 895 | 920 | 919 | 920 | 923 |
由此可以看出xnb的確載入速度更快。
結論:
如果遊戲的物件沒有特殊需求,全都放在一張圖檔裡載入的速度是最快的。
而一張圖載入的速度和xnb檔案大小成正比。
xnb檔案大小直接從size決定。
這樣在用XNA做遊戲時對於資源的運用就會有個底了!