Optimus:在cocos2d中如何高效使用圖片總結
使用Photoshop等工具制作單張圖片,放在Resources目錄下某個或幾個目錄下,使用TexturePacker打包圖片成pvr.ccz文件,使用CCSpriteBatchNode優化繪制,使用FrameCache緩存讀取,使用spriteWithFrameName獲取單張圖片。
免責申明(必讀!):本博客提供的所有教程的翻譯原稿均來自於互聯網,僅供學習交流之用,切勿進行商業傳播。同時,轉載時不要移除本申明。如產生任何糾紛,均與本博客所有人、發表該翻譯稿之人無任何關系。謝謝合作!
前言:這篇文章是我翻譯的第一篇關於cocos2d的文章,我在翻譯的時候盡量按原文意思來,但難免會加入自己一些理解進去。還有這篇文章所采用的xcode版本是3.2.5,用xcode4.0的朋友可能實現起來有點出入,我會在文章的最后給出解決辦法。
在cocos2d里面,為了使你的游戲獲得最佳性能,你需要把許多小的sprite圖片組合到一張大圖里面,這張大圖就叫做sprite sheet。
如果你使用cocos2d已經有一段時間了的話,你可能已經使用過了一款叫做Zwoptex的工具來幫你生成sprite sheet。Zwoptex是一個非常棒的工具--我在我的很多程序里面使用它,而且確實幫我節省了很多時間。
然后,這里有一款新的工具,叫做Texture Packer.它類似於Zwoptex,也能創建sprite sheet,但是它還有一些非常方便的、很神奇的特性。
這篇文章將以一種教程的形式,講述如何在cocos2d游戲開發使用Texture Packer,同時,你還將學習到如何使用像素格式(pixel formats)、Texture Packer 如何智能地讓你的游戲加載速度更快,運行更流暢,而且還能夠在游戲界面看起來很不錯的前提下使用盡可能少的內存。
澄清:我在發博客之前就從Texture Packer這個工具的作者手中拿到了license key。我當時並沒有保證我會發一篇博文來回報他,但是,在我使用這個工具一段時間之后,它確實為我的應用程序減少了很多需要加載的內存,因此我愛上了這款工具。所以,我想讓你們都了解它。
這篇教程是為那些熟悉cocos2d的人寫的。如果你對cocos2d完全陌生的話,你應該從“怎樣做一個簡單的iphone應用程序”系列和其它一些cocos2d的教程開始。(目前這些鏈接依然是e文,但隨着我翻譯進度的前進,這些也會相應的更新。)
開始
首先,確保你安裝了最新版本的cocos2d(在寫作這篇文章的時候,版本號是 Cocos2D v0.99.5-rc1,在翻譯這篇文章的時候,已經是cocos2d-iphone-1.0.0-beta.tar.gz了)。獲得最新版本非常重要,因為新版本里面增加了對一些新的圖片格式的支持,而這恰恰是這篇文章后面要用到的。
一旦你安裝完之后,你就可以啟動XCode,然后使用cocos2d應用程序模板來新建一個工程並把它命名為TextureFun。
下一步,你需要一些圖片來制作sprite sheets。你可以下載這些我收集來的樣例圖片,然后解壓縮,並把整個解壓縮后的目錄拖到你的TextureFun工程的一個子文件夾下面,如下圖所示:
好,現在你擁有一個工程模板和一些樣例圖片以,是時候使用TexturePacker來制作spritesheet了!
使用TexturePacker來創建spritesheet
你需要做的第一件事情就是下載 Texture Packer的免費版本。請注意,你並不需要購買任何東西,對於這篇教程來說你只需要免費版本就足夠了。
當你下載完之后雙擊,然后點擊“TexturePacker.mpkg”,接下來就會彈出一個窗口,然后按照提示一步步安裝在你的mac上。
在你完成安裝之后,在你的Application文件夾下面找到它並運行。當你看到第一個提示窗口出現的時候,選擇“ Use Essentia”(免費版本)繼續。
現在,點擊工具欄上的“Add Folder”按鈕,然后選擇TextureFun\Art\sprites文件夾。Texture Packer將會加載圖片並且智能地把這些圖片布局在spritesheet中,如下圖所示:
另一方面,你能夠看到所有導入到紋理集(Texture Atlas)中的圖片,當你選中某一個的時候會看到有一個邊框--另一個非常方便的功能!你也能夠把鼠標停留在精靈(sprite)上面,看它是否創建走樣(alias)(走樣(alias)指那些被裁剪(cropping)之后的圖像看起來實際上是一樣的)。如下圖所示:
順便說一下通過點擊“Add Folder”按鈕來添加圖片的一些注意事項。首先,當你像這樣通過增加文件夾的方式來增加精靈后,Texture Packer並不是對每一個精靈增加一個索引(reference),而是對整個文件夾增加了一個索引。這意味着,當你以后向這個文件夾中增加精靈之后,下一次你運行Texture Packer的時候,它會重新根據文件夾下面的所有的精靈來創建spritesheet--多么方便啊!
同時,你也不需要非得把所有的精靈都放在同一個根文件夾下面,你可以按照自己的方式把這些精靈放在不同的子目錄下(比如sprites\animals,sprites\monsters),之后當你從cocos2d中引用它們的時候只需指定相對路徑即可。
最后,需要注意的是,你可以包含多個精靈文件夾--這也是一個非常方便的功能,尤其是在你的游戲非常大的時候,你可以為每一個關卡制作一個spritesheet。
好了,現在讓我們來看一下軟件左邊的一些選項。通過這些選項,你能夠配置spritesheet的大小、布局和輸出格式。首先,讓我們來快速瀏覽一遍控制大小和布局的選項:
- Autosize (默認) – 這個選項會為你的spritesheet挑選最小的2的指數倍的大小。這是一個非常方便的特性,因為它可以省去你自己去計算spritesheet大小的時間。
- Min/max size 讓你為你的spritesheet指定一個最大值。如果你想設置特定大小的spritesheet的時候,這也是一個非常方便的特性。(因為對於特定的設備來說,你可不想超過設備能夠支持的最大限制,比如2代touch最大支持texture大小為1024*1024)
- Scale 讓你可以保存一個比原始圖片尺寸要大一點、或者小一點的spritesheet。比如,如果你想在spritesheet中加載“@2x"的圖片(也即為Retina-display設備或者ipad創建的)。但是你同時也想為不支持高清顯示的iphone和touch制作spritesheet,這時候只需要設置scale為 1.0,同時勾選autoSD就可以了。也就是說,只需要美工提供高清顯示的圖片,用這個軟件可以自己為你生成高清和普清的圖片。
- Algorithm TexturePacker里面目前唯一支持的算法就是MaxRects,即按精靈尺寸大小排列,但是這個算法效果非常好,因此你不用管它。
- Border/shape padding 即在spritesheet里面,設置精靈與精靈之間的間隔。如果你在你的游戲當中看到精靈的旁邊有一些“雜圖”的時候,你就可以增加這個精靈之間的間隔。
- Extrude 精靈邊界的重復像素個數. 這個與間隔是相對應的--如果你在你的精靈的邊邊上看到一些透明的小點點,你就可以通過把這個值設設置大一點。
- Trim 通過移除精靈四周的透明區域使之更好地放在spritesheet中去。不要擔心,這些透明的區域僅僅是為了使spritesheet里面的精靈緊湊一點。--當你從cocos2d里面去讀取這些精靈的時候,這些透明區域仍然在尋里。(因為,有些情況下,你可能需要這些信息來確定精靈的位置)
- Shape outlines 把這個選項打開,那么就能看到精靈的邊邊。這在調試的時候非常有用。
對於spritesheet來說,上面提到的各個選項的默認值,你一個也不需要改變--因為它們本來就已經很好了。然后,在輸出部分,你需要改變一些設置。但是在講到那個之前,讓我們先談一談cocos2d中的像素格式。
cocos2d和像素格式
在cocos2d里面,理解像素格式非常重要。因為,像素格式會影響在你的游戲中加載一張圖片到底需要多少內存。因為游戲通常要加載大量的圖片資源,所以你要盡可能充分利用移動設備上面非常少的可用物理內存。
默認情況下面,當你在cocos2d里面加載一張圖片的時候,對於每一個像素點使用4個byte來表示--1個byte(8位)代表red,另外3個byte分別代表green、blue和alpha透明通道。這個就簡稱RGBA8888。
因此,如果你使用默認的像素格式來加載圖片的話,你可以通過下面的公式來計算出將要消耗多少內存來加載:
圖像寬度(width)×圖像高度(height)×每一個像素的位數(bytes per pixel) = 內存大小
此時,如果你有一張512×512的圖片,那么當你使用默認的像素格式去加載它的話,那么將耗費
512×512×4=1MB(好多啊!)
這里,我們以Iphone3G為例。它總共只有128兆內存,但是系統就要占掉一大半,還有其它一些程序也要使用一些內存,實際可用的內存更少。對於單獨一張spritesheet來說那確實足夠了。可是想像一下你有許許多多的spritesheet,而且游戲里面經常需要大量的spritesheet!
這里就需要讓像素格式來幫忙了。你可以為圖片的每個像素點指定更小的字節來保存圖片。(比如每個像素點2個字節,即每個像素點16位),這種方式就能夠在圖片質量和內存消耗之間取得一個很好的平衡點。
通常,你是在你的游戲看起來還ok的提前下,盡可能少地使用內存。背景圖片就非常適用用8位或者16位來存儲,而精靈則一般要用16位或者32位。對於更多可選的像素格式和適用的場合,你可以參考Riq(cocos2d的作者)的一篇文章: understanding pixel format guide.(理解像素格式向導)
順便說一下,如果你注意看窗口的右下角,你會看到Texture Packer會基於你當前選擇的像素格式計算出這張spritesheet所消耗的內存大小,因此你不必手動計算了。:)
像素格式和抖動
很多時候,當你使用較小的像素格式來加載圖片的時候,你會發現圖片的質量也在相應的降低。這時你會看到圖像存在許多顏色的梯度變化。這里有一個例子,展示了當你使用像素格式RBGA4444去顯示一張圖片的時候會是什么樣子:
看到沒有,圖像上面有許許多多的“條條”和顏色梯度變化,特別是熊和綠色的框框那里。
這時,你可能想重新設計你的圖片來確保使用更少的梯度,或者使用大一點的像素格式。但是,在這里,TexturePacker實現了另外一個殺手鐧功能--圖像抖動。
當你使用TexturePacker來保存spritesheet的時候,你可以指定目標像素格式為RGBA4444,然后選擇“dithering method”。這個默認選項會修改一些你的圖像的顏色,但是當有梯度變化或者其它一些會帶來問題的顏色以后,圖像看起來就會非常糟糕。
繼續,我們為spritesheet選擇RBGA4444格式,然后改變抖動選項為“FloydSteinberg+Alpha”。Texture Packer將會在動態修改你的圖片,而且馬上顯示出效果來。和上面的圖片相比,是不是好看多了?
現在讓我們保存這個spritesheet。點擊Texture file旁邊的“...”,在彈出的對話框中選擇TextureFun\Resouces目錄,然后命名為“sprites-hd.pvr.ccz”。然后,TexturePack會自動為我們在Data file那里生成相應的plist文件路徑。並且會命名為“sprites-hd.plist”,這個名字是根據前面你提要的名字來命名的。
“但是,等一下!”,你可能會說,“為什么是pvr.ccz?!”。好吧,我很高興你會這樣問。。。
PVRs和壓縮
PVR圖像是專門為ios設備上面的PowerVR圖形芯片指定的圖像容器。它們在ios設備上非常好用,因為可以直接加載到顯卡上面,而不需要經過中間的轉化。
PVR圖像也可以包含許多種不同像素格式的圖像數據。之前,cocos2d僅僅支持一些用 sdk指定的 texturetool app來創建的圖片格式,不過后來cocos2d已經擴展了許多格式了。
而且,最近cocos2d更新到了可以支持壓縮了的pvr圖像格式pvr.ccz。使用這種圖片格式的好處有兩點:一、可以使你的應用程序更小,因為圖片是壓縮過了的。二、你的游戲能夠啟動地更快。
總而言之,對於spritesheet來說,你可能通過指定16位的像素格式來減少內存消耗,同時保存為pvr.ccz格式來使程序加載速度更快。最后,點擊“Publish”按鈕,你的spritesheet和屬性列表文件就生成好了。Teture Packer會提示你,一些精靈將會創建成紅色(因為你使用的是免費版本)。
優化背景圖片
現在,讓我們也來加載並優化一下我們的背景圖片。點擊new創建一個新的Texture Packer窗口,然后點擊“Add Folder”,並且選擇“TextureFun\Art\flower”文件夾。
把圖片格式改成RBG565(對於大的圖片來說,你可能需要更好的質量),然后改變抖動方法為“FloydSteinberg”(為什么不是FloydSteinberg+Alpha呢?因為像素格式是RBG565,沒有了Alpha通道)。然后指定保存texture file的路徑為“TextureFun\Resouces\flower-hd.pvr.ccz”。最后,點擊“Publish”,關閉警告信息,這時你的屏幕看起來會是下面這樣:
在cocos2d里面使用spritesheet
現在回到我們的項目,右鍵點擊Resources,然后選擇“Add\Existing Files...”。選擇flower-hd.plist, lower-hd.pvr.ccz, sprites-hd.plist, and sprites-hd.pvr.ccz。同時,確保沒有選中“ Copy items into destination group’s folder (if needed)”,然后單擊完成。
下一步,打開HelloWorldScene.m,並且用下面的代碼替換掉你的init方法里的內容:
{
if ( (self = [super init] )) {
CGSize winSize = [CCDirector sharedDirector].winSize;
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGB565];
CCSprite * background = [CCSprite spriteWithFile: @" flower-hd.pvr.ccz " ];
background.anchorPoint = ccp( 0 , 0 );
[self addChild:background];
// More coming here soon...
}
return self;
}
你需要做的第一件事情就是加載背景圖片。首先,你告訴cocos2d使用RBG565的像素格式(你正在為你的背景圖片的每個像素使用8位),然后調用spriteWithFile從磁盤上加載pvr.ccz格式的圖片。注意,這里你並不需要把它當作一個spritesheet(比如,加載plist文件),因為這里“spritesheet”就只有一張圖片。
注意,其實你在加載pvr.ccz格式的文件的時候並不需要指定像素格式,因為這個文件格式本身就包含了這樣一些信息。但是,我們還是顯示地在這里指 定了像素格式,因為如果你加載png格式的圖片的話,(png格式圖片總是保存為每個像素32位,盡管你可能會使用不同的像素格式把它加載到內存里)。
下面,讓我們在“more coming here soon”注釋的地方添加下面的代碼:
CCSpriteBatchNode * spritesBgNode;
spritesBgNode = [CCSpriteBatchNode batchNodeWithFile: @" sprites-hd.pvr.ccz " ];
[self addChild:spritesBgNode];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: @" sprites-hd.plist " ];
這里把像素格式設置為RBGA4444(你為主精靈所使用的每個像素點16位的像素格式),然后為這個spritesheet創建一個batch node。你也需要加載plist文件,把每一個精靈對應的幀(frame)加載到精靈幀緩沖區(sprite frame cache)中。
最后,緊接着上面加入下面的代碼:
for ( int i = 0 ; i < images.count; ++ i) {
NSString * image = [images objectAtIndex:i];
float offsetFraction = (( float )(i + 1 )) / (images.count + 1 );
CGPoint spriteOffset = ccp(winSize.width * offsetFraction, winSize.height / 2 );
CCSprite * sprite = [CCSprite spriteWithSpriteFrameName:image];
sprite.position = spriteOffset;
[spritesBgNode addChild:sprite];
}
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_Default];
這個循環遍歷spritesheet中的所有的圖片,並把他們合理地旋轉在屏幕上面。
如果你使用iPad來編譯並運行你的代碼的話,你會得到下面的運行結果:
這個不就是你想實現的效果嗎--記住,這里的紅色僅僅因為你使用的是免費版本。(ps:正式版本也不貴,100多塊人民幣可以搞定)
但是真正讓人着迷的是那些你看不到的東西。
在背后,你的應用程序加載速度會明顯比以前快很多。而且使用更少的內存,更讓人心動的是,它看起來還是那么棒!而這些功能使用Texture Packer都可以很容易的完成。
不相信我?
當我寫這篇文章的時候,我做了一系列的簡單的測試,從最好的情形到最壞的情形,來測試到底我的程序是如何運轉的。下面是我得出的一些結論:
- 做最原始的事情。使用默認像素格式單個單個精靈地加載,不使用任何spritesheet。大約花費了0.73秒鍾加載,消耗大約26兆內存。而且當你添加更多的精靈進去的時候,游戲就開始卡了。
- 使用默認的像素格式,並且使用spritesheet:前進了一大步。這樣會使游戲性能更好,同時也會減少內存消耗(因為你會把所有的精靈加載到一張大小的spritesheet的,而opengl使用紋理的大小都是2的指數冪,如果你一張精靈的大小是320×200的話,那么opengl會創建512×512的紋理來加載精靈,這樣就有很多空白的地方。實際上就是浪費了內存。)
- 使用不含抖動的Zwoptex並保存為png格式,同時減少像素格式:這樣可以大幅地減少內存消耗,大約只需要15兆左右)。但是卻增加了程序的啟動時間,上升到大約1秒鍾。我認為可能是由於不得不改變顏色緩沖的緣故吧。另外,圖像顯示的效果並沒有在“像素格式和抖動”一節中的截屏效果那么好。
- 使用通過Texture Packer創建的抖動過的spritesheet並且保存為pvr.ccz格式:這在啟動時間和顯示效果上都前進了一大步!(啟動時間大約只有0.31秒左右,內存也只需要大約17兆左右,我認為這可能是由於內存泄漏,這個問題在現在的版本中已經解決了。
好了,如果你按照上面所講的最佳實踐來做的話,我想你在大部分情況下都會做得非常好。:)如果你想看看我寫的測試程序,也想拿來跑一跑的話,可以點此下載。
Texture Packer 和XCode集成
當使用Texture Packer的時候,你可以像這里介紹的一樣使用GUI工具,但是你還可以把它集成到Xcode構建過程中去。這樣你每一次編譯的時候,它都會自動地(如果沒有更改,就不會更新)為了更新spritesheet。
如果你過去用cocos2d寫過游戲的話,你肯定明白一遍又一遍地重新生成你的spritesheet是那么的煩人!雖然每次可能都只需要幾秒鍾的時間,但是老是這樣重復地做這樣的事,確實很煩。
因此,讓我們更方便地構建我們的工程吧--這里只需要花幾秒鍾時間,但是卻可以為你以后節省大量的時間。右鍵點擊“Resources”,選擇“Add\New File...“,然后選擇 Mac OS X\Other\Shell Script,然后選擇下一點。並命名為PackTextures.sh,單擊完成。
然后使用下面的代碼替換掉PackTextures.sh里面的內容:
TP = " /usr/local/bin/TexturePacker "
if [ " ${ACTION} " = " clean " ]
then
echo " cleaning... "
rm resources / sprites - hd.pvr.ccz
rm resources / sprites - hd.plist
rm resources / flower - hd.pvr.ccz
rm resources / flower - hd.plist
# ....
# add all files to be removed in clean phase
# ....
else
echo " building... "
# create hd assets
${TP} -- smart - update \
-- format cocos2d \
-- data resources / sprites - hd.plist \
-- sheet resources / sprites - hd.pvr.ccz \
-- dither - fs - alpha \
-- opt RGBA4444 \
Art / sprites /* .png
${TP} -- smart - update \
-- format cocos2d \
-- data resources / flower - hd.plist \
-- sheet resources / flower - hd.pvr.ccz \
-- dither - fs - alpha \
-- opt RGB565 \
Art / flower /* .png
# ....
# add other sheets to create here
# ....
fi
exit 0
所有Texture Packer GUI界面能夠做的事情,命令行工具也能做。如果你在命令行里面輸入“TexturePacker”,你將會看到一系列它能夠接收的參數說明。
這個腳本僅僅通過運行TexturePacker來從你的Art目錄下讀取精靈文件並創建spritesheet--就像你之前用GUI工具所做的一樣。你可以通過查看TexturePacker命令行工具幫助來獲得更多有關每個參數具體的用法。
接下來,你需要讓你的工程在編譯的時間能夠運行這個腳本。右鍵點擊Targets,選擇“Add\New Target...”,然后選擇“External Target”(不是Shell Script Target),然后點擊下一步,重命名這個Target為TexturePacker,最后點擊Finish。
然后在你新建的target上面雙擊,然后把里面的內容設置成下圖所示:
最后一步就是把這個target設置成你的程序的一個依賴。在TextureFun target上面雙擊,然后選擇General tab,再在Direct Dependencies窗口下面選擇+按鈕。然后從列表中選擇TexturePacker,最后單擊Add Target。如圖下所示:
編譯並運行你的程序,你將會從你的構建結果信息中看到Texture Packer的一些輸出信息,由此來判斷是否一切運轉良好。
如果你看到這些輸出信息的話,那么意味着如果你想要增加一些新的文件到spritesheet中,你只需要把這些新的文件拖到指定的文件下面(這里是Art目錄),然后重新編譯一下,那么就會自動生成新的spritesheet。相反,如果你要移除一些精靈圖片,再重新編譯一下,也ok。是不是非常方便?
Texture Packer vs. Zwoptex
首先,讓我們再說一遍,我是Zwoptex的超級粉絲。我認為Robert已經做了一件非常好的事情來把這些東西整合到一起,而且我老實說,如果沒有這個工具,cocos2d不會走到今天這一步!
然后,當我們來比較Texture Packer和Zwoptex的特性的時候,Texture Packer似乎包含了Zwoptex90%的功能。但是,Texture Packer有三個殺手鐧級別的功能是Zwoptex所沒有的。
- 抖動,抖動,抖動. 噢,我是多么喜歡抖動啊!在過去,有時我想使用比較低的像素格式,但是我不能,因為它看起來效果很不好。但是Texture Packer內置的抖動功能使得圖片看起來還是那么棒,盡管此時的圖片質量很低。
- pvr.ccz 支持. 我喜歡這個特性. 它確實能夠使游戲啟動得更快, 而且會使你的可執行程序更小. 現在我不用花很長時間來上傳和下載我以前寫的應用程序了,我可以很快的下載並更新。
- 命令行工具支持. 一旦你花一點時間把Texture Packer集成到你的Xcode中去,你將會熱愛生活。它是如此地方便,特別是在整個開發過程中,美工對圖片改來改去的時候。
盡管Zwoptex非常棒, 但是它目前為此還是沒有提供我這里列舉的這些功能特性。
Texture Packer 有一點點貴 ($17.95 vs. Zwoptex’s $14.95), 但是,我認為多花這點錢值得。而且就像 Steffen Itterheim 所說, 2個工具都有所長,都做了一件了不起的工作。
接下來該怎么做呢?
這里有上面的教程中所使用的示例代碼。
你是一個Texture Packer粉絲 還是一個Zwoptex粉絲呢?不妨在下面一起聊聊吧!或者你有其它一些很好的策略來高效地在cocos2d里面加載紋理的話,請讓我知道。:)
ps:使用xcode4的朋友,主要需要注意的就是新增加的Target的參數的一些設置。因為xcode4生成的項目文件夾下並不是直接包含了所有的項目文件,而是一個工程文件和另外一個和項目名字一模一樣的文件夾,然后所有的項目有關的源文件和資源文件都放在那個目錄下,所以,我們增加的Target里的設置信息應該改成:
"$(PROJECT_DIR)/TextureFun/Resources/PackTexture.sh"
$(SRCROOT)/TextureFun
其實可能需要注意的就是自動生成普清spritesheet,選中AutoSD選項就行了。
但是PackTexture.sh腳本里要相應增加
${TP}--smart-update \
--format cocos2d \
--data resources/sprites-hd.plist \
--sheet resources/sprites-hd.pvr.ccz \
--dither-fs-alpha \
--opt RGBA4444 \
--auto-sd \ #只要增加這一行就可以自動生成普清的圖片了
Art/sprites/*.png
還有一些軟件的設置選項,大家可以參照軟件自帶的幫助文檔。
如果大家在實踐的過程中,遇到一些問題,可以留言。
著作權聲明:本文由http://www.cnblogs.com/andyque翻譯,歡迎轉載分享。請尊重作者勞動,轉載時保留該聲明和作者博客鏈接,謝謝!