ccsprite是cocos2d遊戲開發中最常用的類,用圖檔把精靈(sprite)顯示在螢幕上。
在遊戲開發中,經常會遇到精靈(sprite)這個術語。精靈是一個圖像,可以在螢幕上獨立地移動。一個精靈可能是玩家角色、敵人,或者是大的背景圖像。一般情況下,精靈來自于開發者所準備的png或pvrtc圖像。一旦圖像載入記憶體,就會将精靈轉換成紋理圖,進而被iphone gpu用于在螢幕上渲染。
3.6.1 ccsprite類的屬性及方法
生成精靈的最簡單方法是把圖檔檔案加載進cctexture2d紋理,然後将它賦給精靈。精靈可以接受其他精靈作為子節點。ccsprite預設錨點位置是(0.5,0.5),也就是節點的幾何中心。
如果一個精靈的父類是标準ccnode節點,那麼它和其他ccnode的屬性和方法沒有太大差別。如果一個精靈的父類或任何一個祖先類是ccspritebatchnode,那麼它具備的特性包括:更快地紋理渲染、不支援camera、不支援gridbase、不能單獨設定alias/antialias屬性、不能單獨設定混合模式、不支援視差滾動。
1 . ccsprite類的屬性
除了從ccnode類繼承的屬性外,ccsprite節點還有如下屬性:
atlasindex:紋理集的索引,通常不建議修改該值。變量類型是nsuinteger。
batchnode:對渲染ccsprite的ccspritebatchnode的弱引用(關于ccspritebatchnode将在3.6.2節介紹),變量類型是ccspritebatchnode。
blendfunc:遵循cctextureprotocol協定。變量類型是ccblendfunc(紋理的混合方法)。
color:rgb色彩,遵循ccrgbaprotocol協定。變量類型是cccolor3b(使用3位元組組成的色彩資訊,分别代表r、g、b)。
dirty:判斷精靈是否需要在紋理集中更新。變量類型是bool。
flipx:判斷精靈是否會沿水準方向翻轉。變量類型是bool。
該屬性隻會對精靈的紋理進行翻轉,不會影響到精靈的子節點,同時也不會修改錨點位置。如果想要同時修改錨點并對子節點進行翻轉,我們可以使用下面的方法:
flipy:判斷精靈是否會沿垂直方向翻轉。變量類型是bool。
sprite.scaley *= -1;
offsetposition:以點值計量的精靈位置的偏移,如果使用zwoptex之類的編輯器,會自動計算該數值。變量類型是cgpoint。
opacity:透明度,遵循ccrgbaprotocal協定。變量類型是glubyte。
quad:方形的資訊(紋理坐标、頂點坐标、色彩等)。變量類型是ccv3f_c4b_t2f_quad。
textureatlas:到cctextureatlas的弱引用,該屬性僅在該精靈使用ccspritebatchnode渲染時可用。變量類型是cctextureatlas。
texturerect:以點值傳回精靈對象的矩形框。變量類型是cgrect。
texturerectrotated:紋理矩形是否被旋轉。變量類型是bool。
2 . ccsprite類的方法
除了從ccnode繼承的方法,ccsprite還支援以下常用方法:
(1)-(id) initwithbatchnode:(ccspritebatchnode*)batchnode rect:(cgrect)rect
該方法使用ccspritebatchnode和矩形框(以點數表示)初始化一個精靈對象,具體實作代碼可參考模闆中的ccsprite.m檔案。
正如之前所提到的,對開發者來說,更多的是使用類方法來直接建立所需要的對象。
該方法的一個變化是使用ccspritebatchnode和矩形(以像素值表示)初始化一個精靈對象,示例代碼如下:
(2)-(id)initwithcgimage:(cgimageref)imagekey:(nsstring *)key
該方法使用cgimageref和鍵值初始化一個精靈對象,cctexturecache将使用該鍵值判斷某個紋理是否已使用cgimage建立。例如某個有效鍵值可能是“sprite_frame_01”,如果鍵值是nil,則每次使用cctexturecache建立一個新的紋理。
(3)-(id)initwithfile:(nsstring *)filenamerect:(cgrect)rect
該方法使用圖檔名稱和矩形初始化一個精靈對象,位置偏移是(0,0)。該方法有一個變化,就是不指定rect,使用圖檔的大小作為矩形,示例代碼如下:
(4)- (id) initwithspriteframe: (ccspriteframe *) spriteframe
該方法使用精靈幀初始化一個精靈。
(5)- (id) initwithspriteframename: (nsstring *) spriteframename
該方法使用精靈幀的名稱初始化一個精靈。使用該方法時,将從ccspriteframecache中擷取一個ccspriteframe;如果不存在,系統将抛出一個異常。
(6)- (id) initwithtexture: (cctexture2d *)texturerect: (cgrect) rect
該方法使用紋理和矩形初始化一個精靈,位置偏移是(0,0)。該方法還有一個變化,就是使用紋理的大小作為矩形。示例代碼如下:
(7)- (ccspriteframe*) displayedframe
該方法傳回目前所顯示的精靈幀。示例代碼如下:
(8)-(bool) isframedisplayed:(ccspriteframe*)frame
該方法判斷某個ccspriteframe對象是否正在被顯示。示例代碼如下:
(9)-(void) setdisplayframe:(ccspriteframe*)frame
該方法為ccsprite精靈設定新的顯示幀。示例代碼如下:
(10)- (void) setdisplayframewithanimationname: (nsstring *) animationname
index: (int) frameindex
該方法根據動畫名稱和索引更改要顯示的幀,動畫名将從ccanimationcache中擷取。示例代碼如下:
(11)- (void) settexturerect: (cgrect) rect
該方法更新ccsprite精靈對象的紋理矩形大小。示例代碼如下:
注意 使用了settexturerect再使用contentsize時要注意,不能再當成初始化時的contentsize大小來使用。
這個方法在實際開發中的作用何在呢?想象用cocos2d開發一款rpg小遊戲,遊戲中有很多角色,而每個角色都有顯示自己生命值的血條,我們隻需在預定消息的方法中使用settexturerect,就可以實時更改角色的血條長度。
(12)- (void) settexturerect: (cgrect) rect
該方法更新ccsprite精靈對象的紋理矩形,矩形旋轉和未裁剪大小。示例代碼如下:
(13)+(id)spritewithbatchnode:(ccspritebatchnode *) batchnode
該方法使用ccspritebatchnode和矩形(以點數表示)來建立一個精靈對象。首先看該方法的實作代碼:
關于執行個體方法和類方法在實際使用中的差別前面已經提到,大家隻需要知道大多數情況下直接使用類方法來建立對象就可以了。示例代碼如下:
(14)+(id)spritewithcgimage:(cgimageref)image key:(nsstring *)key
該方法使用cgimageref和鍵值建立一個精靈對象。cctexturecache将使用該鍵值判斷某個紋理是否已使用cgimage建立。例如,某個有效鍵值可能是“sprite_frame_01”,如果鍵值是nil,則會每次使用cctexturecache建立一個新紋理,示例代碼如下:
(15)+(id)spritewithfile:(nsstring *)filename rect:(cgrect)rect
該方法使用圖檔名稱和矩形建立一個精靈對象,位置偏移是(0,0)。該方法的使用頻率相當高,大多數時候使用它建立精靈對象。示例代碼如下:
該方法有一個變化,就是不指定rect,使用圖檔的大小作為矩形。大多數情況下無需特别指定rect的大小。示例代碼如下:
(16)+(id) spritewithspriteframe: (ccspriteframe *) spriteframe
該方法使用精靈幀建立一個精靈。示例代碼如下:
(17)+ (id) spritewithspriteframename: (nsstring *) spriteframename
該方法使用精靈幀的名稱建立一個精靈。該方法在使用時,将從ccspriteframecache中擷取該ccspriteframe,如果精靈幀緩存中不存在該精靈幀,系統将抛出一個異常。示例代碼如下:
(18)+ (id) spritewithtexture: (cctexture2d *)texture rect: (cgrect) rect
該方法使用紋理和矩形建立一個精靈,位置偏移是(0,0)。示例代碼如下:
該方法還有一個變化,就是使用紋理自身的大小作為矩形。示例代碼如下:
(19)-(void)updatetransform
該方法根據旋轉角度、位置和比例值更新方形。示例代碼如下:
注意 隻有當該精靈對象是使用ccspritebatchnode渲染生成時,才可使用該方法。
ccsprite類在cocos2d中起着極其重要的作用,遊戲角色、敵人、場景、道具等幾乎都會使用ccsprite來建立。對于ccsprite類的屬性和方法,大家需要多去嘗試,并在實踐中逐漸體會它的強大!
3.6.2 ccspritebatchnode精靈表單
ccspritebatchnode前身是舊版本cocos2d中的ccspritesheet,也就是精靈表單。在學習ccspritebatchnode之前,需要了解紋理圖集的基本概念。
生活中處處充滿了限制,遊戲開發也不例外。事實上,制約遊戲開發的最大問題在于硬體裝置的性能瓶頸。遊戲開發者最容易遇到的問題就是,當有大量的精靈出現在螢幕上時,遊戲的運作會變得奇慢無比。到目前為止,所有圖像元素都是使用獨立的ccsprite,看起來沒什麼不妥。但是,當螢幕上有15或20個元素時,會發現遊戲幀速率急劇下降;添加的圖像元素越多,幀速率下降越快。即便不提供任何遊戲邏輯機制,隻是簡單地移動這些ccsprite精靈,随着精靈數量的增加,遊戲還是會變得越來越慢。導緻這一問題的原因有很多,最重要的就是在導入圖像元素時,開發者使用了單獨的ccsprite,而不是ccspritebatchnode。
簡而言之,調用過多的opengl es指令、把每個圖像作為單獨的紋理來處理已經超過了gpu所能處理的上限。解決這一問題最簡單的方法就是使用紋理圖集。
如果仔細看看ccsprite相關代碼(ccsprite.h檔案),會發現在遊戲的每一幀都會調用-(void)draw方法。在draw方法中,每次在螢幕中繪制一個精靈時程式都會調用真實的opengl es指令(gl為字首的方法,詳見gl.h);對于每個精靈,opengl es都需要将紋理圖綁定在這個ccsprite上,然後将其繪制在螢幕上(稱為渲染)。
在螢幕上真實顯示圖像的像素前,還有一件事要做:ios提供的opengl es驅動将把opengl es的指令轉換成gpu可以了解的硬體編碼,進而讓gpu顯示圖像。作為一個遊戲開發者,無需了解更多驅動細節,隻需明白一點,每次調用opengl es的指令都将耗費opengl es驅動的cpu時鐘。如果盡可能地減少opengl es的調用,遊戲的運作将更為順暢。
要實作這一點,就需要讓opengl es一次性處理所有紋理圖,而使用ccspritebatchnode和紋理圖集就能實作這一目的。
1 . ccspritebatchnode類的屬性
ccspritebatchnode是cocos2d中一個特殊的類,其中可包含多個ccsprite。它直接繼承自ccnode,其特有的屬性如下:
textureatlas:所使用的紋理圖集。變量類型是cctextureatlas。
blendfunc:混合方法,遵循cctextureprotocol協定。變量類型是ccblendfunc。
descendants:子孫節點。變量類型是ccarray。
2 . ccspritebatchnode類的方法
除了繼承自ccnode的方法,特有的方法如下:
(1)- (id) initwithfile: (nsstring *) fileimagecapacity:(nsuinteger)capacity
該方法使用特定格式的圖檔檔案(png、jpeg、pvr等)初始化一個ccspritebatchnode精靈表單,可以容納29個子節點。在實際運作時如果超出了限制,該數字還可以再增加約33%。圖檔檔案将使用texturemgr加載。
(2)-(id)initwithtexture:(cctexture2d *)texcapacity:(nsuinteger)capacity
該方法使用cctexture2d紋理和指定的子節點容量初始化一個ccspritebatchnode精靈表單。在實際運作時,如果超出了限制,該數字還可以再增加約33%。
(3)+ (id) batchnodewithfile: (nsstring *) fileimage
該方法使用特定格式的圖檔檔案(png、jpeg、pvr等)建立一個ccspritebatchnode精靈表單,可以容納29個子節點。在實際運作時如果超出了限制,該數字還可以再增加約33%。圖檔檔案将使用texturemgr加載。示例代碼如下:
該方法還有一個變化,就是指定子節點的數量。示例代碼如下:
(4)+(id)batchnodewithtexture:(cctexture2d *)tex
該方法使用cctexture2d紋理建立一個ccspritebatchnode精靈表單,可以容納29個子節點。在實際運作時如果超出了限制,該數字還可以再增加約33%。示例代碼如下:
(5)- (void) removechild: (ccsprite *) spritecleanup: (bool) docleanup
該方法從ccspritebatchnode精靈表單中删除子精靈,由于這種操作速度非常慢,通常不推薦使用。示例代碼如下:
該方法還有一個變化,就是根據指定的索引删除子精靈。示例代碼如下:
本書進階篇會詳細說明如何使用紋理貼圖和ccspritebatchnode實際提升遊戲的性能。
注意 在使用精靈表單時,ccspritebatchnode、ccspriteframe和ccspriteframecache常常配合使用。
3.6.3 ccspriteframe精靈幀
與我們之前了解的節點類不同,ccspriteframe和ccspriteframecache都直接繼承自objective-c中的nsobject。
ccspriteframe(精靈幀)中主要包括cctexture2d紋理、矩形大小,用來表示一個精靈。
1 . ccspriteframe精靈幀的屬性
ccspriteframe(精靈幀)的主要屬性有如下幾個:
rect和rectinpixels:精靈幀的矩形大小,分别用點值和像素值來表示。這兩個屬性是彼此相連的,任一個屬性的變化都會自動更新另一個屬性。變量類型是cgrect。
rotated:精靈幀的矩形框是否發生旋轉。變量類型是bool。
offset和offsetinpixels:精靈幀的位置偏移(以像素值計算)。變量類型是cgpoint。
originalsize和originalsizeinpixels:修剪後圖檔的原始大小(以像素值計算)。變量類型是cgsize。
texture:精靈幀的紋理。變量類型是cctexture2d。
texturefilename:精靈幀的紋理檔案名稱。變量類型是nsstring。
2 . ccspriteframe精靈幀的方法
ccspriteframe有兩個執行個體方法和兩個靜态方法(類方法),分别如下:
(1)-(id)initwithtexture:(cctexture2d *)texturerect: (cgrect) rect
該方法使用紋理、矩形(以點值表示)兩個屬性初始化一個ccspriteframe。使用此方法時,假定精靈幀未被裁剪過。
(2)-(id)initwithtexture:(cctexture2d *)texture
該方法使用紋理、矩形(以像素值表示)、是否旋轉、位置偏移和原始大小等屬性初始化一個ccspriteframe,此處的原始大小是未被裁剪前的精靈幀大小。
(3)+(id)framewithtexture:(cctexture2d *)texture
該方法使用紋理、矩形(以點值表示)兩個屬性建立一個ccspriteframe。使用此方法時,假定精靈幀未被裁剪過。示例代碼如下:
ccspriteframe *frame1 = [ccspriteframe framewithtexture:spritesheet.texture rect:cgrectmake(228, 0, 75, 100)];
(4)+(id)framewithtexture:(cctexture2d *)texture
該方法使用紋理、矩形(以像素值表示)、是否旋轉、位置偏移和原始大小等屬性建立一個ccspriteframe,此處的原始大小是未被裁剪前的精靈幀大小。示例代碼如下:
3.6.4 ccspriteframecache精靈幀緩存
ccspriteframecache(精靈幀緩存)主要用來存放ccspriteframe,是以它沒有提供特别的屬性,而是提供一系列用于管理ccspriteframe的方法,具體如下。
(1)- (void) addspriteframe: (ccspriteframe *) frame
該方法使用指定名稱添加一個精靈幀。如果該名稱在ccspriteframecache中已存在,則原有内容将被新精靈幀所替代。示例代碼如下:
(2)- (void) addspriteframeswithdictionary: (nsdictionary *) dictionary
texture: (cctexture2d *) texture
該方法使用詞典添加多個精靈幀,紋理将和所建立的精靈幀關聯在一起。示例代碼如下:
(3)- (void) addspriteframeswithdictionary: (nsdictionary *) dictionary
該方法使用詞典添加多個精靈幀,紋理檔案名稱将和所建立的精靈幀關聯在一起。示例代碼如下:
(4)- (void) addspriteframeswithfile: (nsstring *) plist
該方法從plist屬性檔案中添加多個精靈幀。紋理将會被自動載入,紋理的擴充名則使用.png替代.plist。如果需要添加其他紋理,請使用addspriteframeswithfile:texture方法。示例代碼如下:
(5)- (void) addspriteframeswithfile: (nsstring *) plist
該方法從plist檔案中添加多個精靈幀,紋理檔案名将和所建立的精靈幀關聯在一起。示例代碼如下:
(6)+ (void) purgesharedspriteframecache
該方法清除ccspriteframecache中的緩存,删除所有精靈幀,并釋放所保留的變量。示例代碼如下:
(7)- (void) removespriteframebyname: (nsstring *)name
該方法根據名稱從精靈幀緩存中删除一個精靈幀。示例代碼如下:
(8)- (void) removespriteframes
該方法清除已加載精靈幀的詞典。當收到記憶體警報時會調用此方法,短時間内的作用是避免應用被ios強行關閉,中長期的作用是配置設定更多的資源。示例代碼如下:
該方法還有一個變化,是從nsdictionary中删除多個精靈幀。示例代碼如下:
(9)- (void) removespriteframesfromfile: (nsstring *) plist
該方法從plist屬性檔案中删除多個精靈幀。存儲在該plist檔案中的精靈幀都會被删除。示例代碼如下:
(10)- (void) removespriteframesfromtexture: (cctexture2d *) texture
該方法删除所有與特定紋理相關聯的精靈幀。示例代碼如下:
(11)-(void)removeunusedspriteframes
該方法删除所有未使用的精靈幀,所有retain計數為1的精靈幀都會被删除。當遊戲切換新場景時可以調用此方法。示例代碼如下:
-(ccspriteframe )spriteframebyname:(nsstring )name;
傳回之前添加的某個精靈幀,如果未找到對應名稱的精靈幀,将傳回nil。如果要使用該方法,需要retain這個精靈幀。示例代碼如下:
(12)+(ccspriteframecache *)sharedspriteframecache
該方法擷取精靈幀緩存的單例對象。示例代碼如下:
(13)-(ccspriteframe)spriteframebyname:(nsstring)name
該方法使用檔案名擷取此前添加的精靈幀。示例代碼如下: