在完成了第一個hellococos2d項目後,如果讀者不僅想看到飛機在螢幕上飛行,還想知道這一切是怎樣實作的,我們不妨來一起探究其中的每一行代碼。
1.4.1 初識場景和節點
要想了解hellococos2d這個項目,首先要了解場景(ccscene)、層(cclayer)和節點(ccnode)的概念。
cocos2d遊戲是由不同的場景構成的,由導演(ccdirector)負責運作和切換各個場景。在cocos2d中,ccdirector在任何一個時間點上隻能運作一個場景。
所有節點的父類都是ccnode類,它包含了位置資訊,但沒有顯示資訊。它是所有其他節點類的父類,包括兩個最基本的類:ccscene和cclayer。
ccscene是一個抽象的概念,功能是根據像素坐标把物體放置在場景裡相應的地方。是以任何cocos2d場景都會用一個ccscene作為父對象。
cclayer類本身并不做什麼,它的功能是允許觸摸和加速計的輸入。因為大多數遊戲會接受基本的觸摸輸入,是以cclayer通常是第一個被加入ccscene的類。
cocos2d的每個場景都包含一個或多個層,彼此疊加。
舉例來說,通常我們剛進入遊戲的時候都會看到一個初始菜單,接下來會進入遊戲的主畫面來戰鬥或完成任務,然後很快會因為種種原因看到gameover這個經典永恒的畫面。而在不同的場景之中,可能會有很多的層(就像photoshop的圖層一樣),每個層裡又包含了很多nodes(子節點,比如精靈、标簽、菜單等),每個子節點也可能有其他的節點(比如一個精靈的裡面可以存在一個子精靈)。
請記住,當需要在螢幕中添加飛機精靈的時候,要先建立一個新的精靈對象,然後把它添加成該層的子節點。
1.4.2 實作代碼分析
現在逐漸分析1.3節的代碼是如何實作的。
1 . 主程式入口main.m
萬物皆有起始,程式的運作也是。毫無疑問,任何一個使用xcode開發的應用或遊戲都是從main.m開始執行的。本例中的main.m在左側的supporting files檔案夾下,如圖1-20所示。

main.m中的代碼如代碼清單1-3所示。
代碼清單1-3 main.m中的代碼
簡單來說,主函數建立了一個nsautoreleasepool(自動釋放池),然後調用uiapplicationmain啟動程式,該程式使用appcontroller類來執行uiapplicationdelegate的協定(protocol)。
上述代碼使用了nsautoreleasepool來管理記憶體。也就是說,通過給對象發送autorelease(自動釋放)消息,就不再需要擔心忘記發送release消息。autorelease pool(自動釋放池)可以確定記憶體中的自動釋放對象最終會被釋放。
注意 main.m檔案的内容對于任何ios應用或遊戲都是一樣的,無須對此做任何的修改。如果你看不懂這個檔案,建議學習iphone或者objective-c開發的基礎教程。
簡而言之,程式啟動了,接下來會調用appcontroller類。
在此之前,可以先看supporting files下的另一個檔案prefix.pch,這個頭檔案的作用是給編譯過程加速。我們應該把不常變化的架構(frameworks)頭檔案添加到字首頭檔案(prefix header)中。這樣,在編譯的時候,架構的代碼會被預先編譯,所有的類都将可以使用這些頭檔案。
不過,這樣做也有一個缺點:如果字首頭檔案中一個頭檔案發生了變化,項目中的所有代碼都将會重新編譯。這就是為什麼應該隻添加那些極少或者從來都不變化的頭檔案到字首頭檔案中。
我們可以将cocos2d.h頭檔案添加到字首頭檔案中,因為它很少改變。不過,隻有複雜一些的項目才會感受到編譯速度的變化。
如果大家想了解更多關于如何使用字首頭檔案減少編譯時間的知識,可以參考蘋果開發者文檔。
2 . appdelegate
每個ios程式都有一個appdelegate類,用于實作uiapplicationdelegate協定。appdelegate在某些時間點通過從ios接收資訊來跟蹤程式的狀态變化。例如,可以用它确認是否有打進來的電話或者系統記憶體是否已經不足。在檢視以上方法的具體實作代碼前,先要了解appdelegate.h中的類定義。在appdelegate.h中定義了appcontroller這個類,它直接繼承自nsobject,且遵循uiapplicationdelegate和ccdirectordelegate兩個協定。
程式開始運作後,收到的第一個消息就是applicationdidfinishlaunchingwithoptions,在這個方法中,可以放置項目的啟動代碼。cocos2d就是在這裡初始化的。
在xcode中打開appdelegate.m檔案,讓我們來詳細檢視并解釋其中的代碼細節。
先說一下在xcode4中切換方法的技巧,如圖1-21所示。單擊最右側的“no selection”處,就可以在不同方法間跳轉。這跟xcode3.2.6是不同的,用起來一樣友善。
現在切換到applicationdidfinishlaunchingwithoptions方法,如代碼清單1-4所示。
代碼清單1-4 applicationdidfinishlaunchingwithoptions方法
以上的代碼中,第一步就是對uiwindow進行初始化,并綁定ccglview。uiwindow被設定為全屏,而ccglview則會接收所有的opengl es指令調用。接下來,我們按照注釋的編号來逐行解釋。
1)建立程式的主視窗。
2)建立ccglview對象,用于遊戲中的渲染。在cocos2d中,使用ccglview将opengl es指令發送給opengl es驅動。從cocos2d v2.0之後,ccglview替代了之前的eaglview,以使命名更加規範。
3)将fps(frames per second)和spf(second per frame)顯示設定為開。cocos2d會自動計算遊戲的目前幀數,并顯示在螢幕的左下角。在調試過程中fps顯示對我們會非常有用,當然在正式上傳之前可以将其設定為no(即不顯示)。
4)将動畫間距設定為每秒60次,這也是cocos2d中的預設設定。通常情況下,cocos2d每秒會更新螢幕中的顯示60次(最高的重新整理頻率)。
5)将openglview綁定到director_導演對象上。
6)将director_的代理對象設定為自身,以便擷取螢幕旋轉等資訊。
7)設定2d或3d投射。
8)啟用retina高清顯示模式。
9)設定cocos2d的紋理格式。
10)在ipad或retina高清顯示模式下,使用ccfileutils自動添加圖檔的字尾。
11)設定pvr圖像具備多重透明度。
12)初始化introlayer場景,并使用pushscene方法切換到該場景。
13)使用director_建立navigation controller。
14)将此導航控制器設定為根視圖控制器。
15)設定主視窗可見。
在cocos2d中,ccdirector負責遊戲的循環運作,并且在遊戲中渲染所有的圖像。是以,它會掌控遊戲的運作、暫停或停止。代碼清單1-5中顯示了使用ccdirector響應iphone作業系統的各個事件,包括暫停或繼續。
代碼清單1-5 調用ccdirector
我們還是按照注釋編号來依次解釋每個方法的作用。
1)當作業系統暫停應用的執行時,調用此方法來暫停遊戲和所有的計時器。當玩家在玩遊戲的過程中鎖定了ipad或iphone,或是有電話打進來,或其他類似事件發生需要強迫遊戲進入背景時,會調用此方法。
2)與1)中的情況相反,通常當玩家解鎖ipad或iphone,或電話已接聽完畢,會調用此方法來繼續遊戲和所有的計時器。
3)當系統收到記憶體不足的警告時,會調用此方法從記憶體中清除未在螢幕中顯示的紋理圖。
注意 所有的圖像檔案(png或pvr格式)都被加載成gpu可以了解的opengl es紋理,而精靈則對應着這些紋理圖。cocos2d内置了一個紋理緩存管理器來儲存這些紋理圖,這樣可以極大地加速建立新精靈并充分利用已有的紋理圖。不利的一面是,如果收到記憶體警報,cocos2d會将目前未使用的紋理圖全部從記憶體中清除。是以,當遊戲的場景切換時,有時需要手動釋放目前的層和場景。
4)在ios4.0及以後的版本中,支援應用的背景運作。在這種情況下,會停止運作螢幕中的動畫。
5)與4)中的情況相反,應用重新回到前台來運作。這種情況下會重新啟動螢幕中的動畫。
6)玩家完全退出應用且不在背景運作時,将調用此方法。該方法會終止ccdirector的控制,并從應用程式的uiwindow中解除對eaglview的綁定。同時還将結束遊戲循環,從記憶體中清除所有的紋理和計時器。
7)将上一次時間調用和目前事件調用間的增量時間設定為0。該方法的調用場景是:兩次時間調用之間已經過了太長的時間。這通常是由iphone重新調整了系統時間而導緻的。
在代碼清單1-4中,最重要的代碼是applicationdidfinishlaunching方法中的如下代碼:
應用在啟動appdelegate後,建立一個ccdirector導演類對象,然後運作introlayer場景。接下來我們看看introlayer的實作。
3 . introlayer
首先,introlayer繼承自cclayer,而ccdirector隻能運作ccscene對象,是以,需要實作introlayer的scene方法,它會把introlayer當作唯一的子節點添加進去,并傳回一個ccscene,具體如代碼清單1-6所示。
代碼清單1-6 introlayer的scene方法
introlayer在以前的cocos2d版本中是不存在的,這是cocos2d v2.0模闆新加的類,它的主要目的就是在進入遊戲之前,可以顯示遊戲制作公司的logo等資訊。顯示logo的實作代碼如代碼清單1-7所示。
代碼清單1-7 introlayer的onenter方法
我們仍然按照注釋編号來依次解釋每個方法的作用。
1)獲得裝置支援的視窗大小。
2)判斷裝置是iphone還是ipad,并據此來加載不同的背景資源圖檔。
3)設定背景圖檔的位置。
4)把背景圖檔添加到introlayer中。
5)觸發一個定時器方法調用,在1秒後會調用maketransition函數。
最後,我們看看maketransition方法的實作,如下所示。
這個方法隻有一句代碼調用,就是使用ccdirector配合cctransitionfade場景切換特效類,把helloworldlayer類在1秒螢幕變白後展示出來。最後,我們看看hellowordlayer類的實作。
4 . helloworldlayer
helloworldlayer類繼承自cclayer。因為ccscene隻是一個抽象的概念,預設設定場景的方法是在類裡使用一個靜态初始化方法+(id)scene。此方法會生成一個ccscene對象,并且将helloworldlayer的對象添加為場景的節點。幾乎在任何情況下,ccscene都是在這裡建立和使用的。
+(id)scene類方法的代碼如代碼清單1-8所示。
代碼清單1-8 +(id)scene類方法
第一行代碼是通過調用[ccscene node]來建立一個ccscene的執行個體化對象,[ccscene node]其實等同于[[[ccscene alloc]init]autorelease];接下來,建立了一個helloworldlayer的層節點;最後,将該層節點添加到場景中,并傳回場景給調用者。
了解了+(id)scene類方法的作用,我們再來看-(id)init初始化方法的作用,其代碼如代碼清單1-9所示。
代碼清單1-9 -(id)init初始化方法
代碼清單1-9中的代碼注釋基本上解釋了每行代碼的具體作用。此外,其中有些看似比較奇怪的地方,如在self = [super init]調用中,發送給super對象的init消息将傳回的值賦給了self。如果大家之前有c++的程式設計經驗,可能會對此很不了解。這是因為在objective-c中,必須手動調用super類的init方法,不存在對父類的自動調用;而且必須把[super init]的傳回值賦給self,因為有可能會得到一個空值(nil)。
[super init]的另一種寫法如下,作用和上面的寫法完全一樣:
最後還有一個-(void)dealloc方法,它的作用和一般的ios應用無異,用于銷毀前面所建立的對象。
以上就是hellococos2d項目的詳細實作過程。考慮到大家之前大多有過ios開發的經驗,這裡并沒有對所有類的所有實作代碼進行逐行解釋和分析,但通過以上三步,我們初步了解了一個cocos2d應用的啟動和運作過程,為以後開發更複雜的應用或遊戲打下了堅實的基礎。