第18節 - LibGDX: Texture Packer
這一節我會介紹Texture Packer,Texture Packer可把不同的圖片打包在一個圖片檔案內,再作其他程式處理。
我在第11節 - LibGDX: World, Texture, Background, Camera, Viewport, Screen & OpenGL說過LibGDX會用到OpenGL(Open Graphics Library)來render圖像。
我也在第12節 - LibGDX: Texture, TextureRegion, SpriteBatch & Sprite說過Texture(紋理)就是把圖片(e.g .png圖片)從原始格式解碼並上傳到GPU,作其他程式處理,就被稱為Texture。
所以如果我們有很多圖片,如果Texture每次只包含一張圖片,並把它解碼和上傳到GPU,這樣做就會用上很多記憶體資源,如下圖:

解決方法是把不同的圖片儲存在一個TextureAtlas類別上,再傳到GPU作其他程式處理,這樣做就會大大減少記憶體的資源。Libgdx有一個Texture Packer小程式 ,它可以幫助我們把不同的圖片(例如:.png圖片)儲存在一個.pack和.png文件上,如下圖:

我將會用以下LibGDX Texture Packer制作一個mainmenu.pack和mainmenu.png檔案,如下圖:

用Texture Packer的好處
第1個好處就是剛剛提過的,把不同的圖片儲存在一個TextureAtlas類別上,再傳到GPU作其他程式處理,這樣做就會大大減少記憶體的資源。
第2個好處就是圖片的大小不須要用上Power of Two(POT, 2的N次方,2^n)的規定。
我在第12節 - LibGDX: Texture, TextureRegion, SpriteBatch & Sprite介紹過Power of Two。
為甚麼圖片尺寸建意是2的N次方圖片呢?
LibGDX或Android是用OpenGL for Embedded Systems (OpenGL ES或GLES)畫圖,OpenGL ES有多個版本:OpenGL ES 1.x, OpenGL ES 2.0和OpenGL ES 3.x 。
它們之間的一個重要區別就是: OpenGL ES 1.x的圖片大小必須是2的N次方(整數),而 OpenGL ES 2.0和OpenGL ES 3.x 則無此要求。而libgdx在早期使用OpenGL ES 1.x的版本,所以只支援2的N次方圖片,並一直沿用至今,LibGDX新版本(0.9.8以上)允許開發者使用OpenGL ES 2.0,所以圖片尺寸是2的N次方就不一定須要。
但是為了不同版本,建意大家用2的N次方圖片。
2的N次方:
1. 2^0=1
2. 2^2=4
3. 2^3=8
4. 2^4=16
5. 2^5=32
6. 2^6=64
7. 2^7=128
8. 2^8=256
9. 2^9=512
10. 2^10=1024等等
注意,圖片是2的N次方,可任意組合,例如: 64x64,64x128,128x256等等。
用LibGDX Texture Packer制作的mainmenu.pack和mainmenu.png檔案

- 進入LibGDX Texture Packer的官方網站,按Download鍵下載。
- 下載gdx-texturepacker-x.x.x.zip檔案(x.x.x是版本號碼)。

- 以上例子會用c:/temp作例子。
- 建立一個input file,它用來儲存原本的.png檔案。
- 建立一個output file,它用來儲存Texture Packer產生出來的mainmenu.pack和mainmenu.png檔案。
- 4,5和6步就是把下載回來的gdx-texturepacker-x.x.x.zip檔案unzip。

- 把所有圖片放在c:/temp/input內。

- 1和2步在c:/temp/內開啟gdx-texturepacker.jar檔案。

- 按New pack鍵。
- 輸入mainmenu。

- 選擇c:\temp\input。
- 選擇c:\temp\output。
- 選擇Force PoT,則是把mainmenu.png圖片的大小用上Power of Two的規定。
注意,在這例子裡,mainmenu.png的大小是512x1024px,Texture Packer程式會自動計算圖片的大小。 - 選擇Pack'em all(則是Pack them all的意思)。

- 完成後,按Close鍵。

- Texture Packer程式會自動產生mainmenu.pack和mainmenu.png檔案,並儲存在c:\temp\output內。
- mainmenu.pack其實只是一個文字檔案,它把c:\temp\input內所有圖片的資料記錄下來,例如:file name, rotate, coordinate, size, origin, offset和平index等等。
- mainmenu.png其實就是一個大的png檔案,它把c:\temp\input內所有圖片儲存下來。
- mainmenu.png的大小是512x1024px,符合Power of Two的大小,Texture Packer程式會自動計算圖片的大小。

- 以上是mainmenu.pack和mainmenu.png檔案內的內容。
用Eclipse制作一個MyDemo18的文件檔案

- 把mainmenu.pack和mainmenu.png檔案複製到Android的assets內。
1 - DesktopLauncher

- DesktopLauncher是PC Desktop的Starter Class,我們在DesktopLauncher內設定顯示的大小為500x700px。
2 - MyDemo18

- 在MyDemo18.java內把MyDemo18 extends Game。
在Game abstract class內create()也未定義內容,所以在MyDemo18 class內必須定義內容。 - 定義create()方法,在create()方法內用setScreen()方法把畫面跳到menuScreen。
3 - MenuScreen

- 在MenuScreen.java內用新的TextureAtlas類別、TextureRegion類別和TextureAtlas內的findRegion()方法,把圖片顯示在屏幕上。
- 建立有關的Member Variables。
- 在show()方法內建立一個TextureAtlas物件(atlas),再把mainmenu.png的圖片傳入。
再用atlas.findRegion()方法把mainmenu.png內的不同圖片儲存到不同的TextureRegion物件內。 - 設定不同圖片的位置。
- 顯示不同圖片到屏幕上。
- 注意,Screen介面的話dispose()方法和Game類別的dispose()方法不同,根據LibGDX官方文件解釋,Screen介面的dispose()方法是不會自動執行,所以我們必須在畫面轉換時在hide()執行dispose()方法,我會在第24節 - LibGDX: Disposable Interface詳細介紹。
- 最後別忘記把在dispose()方法內輸入batch.dispose();和altas.dispose()。
注意,以上Gdx.gl.glClearColor(0x64/255.0f, 0x95/255.0f, 0xed/255.0f, 0xff/255.0f);是把背景顏色設定為淺藍色。

- 以上就是執行結果。
圖片的座標位置


單一圖片用Texture類別和多圖片用TextureAtlas類別的分別
