本次我們将帶領大家手動完成一個簡單但功能完整的打飛機遊戲,實作飛機飛行、飛機碰撞、發射子彈、敵機發射大子彈、背景音樂、子彈音效、分數統計、菜單管理等功能。它雖然不會為你赢得什麼獎項,但是可以總結前面所學的所有知識,幫助大家更好地掌握cocos2d基本對象的使用,同時體驗cocos2d的強大以及易用性。
首先打開Xcode,使用cocos2d iOS模闆建立一個項目,命名為“AirfightGame”,然後選擇一個目錄,單擊“Create”按鈕。為cocos2d項目的源代碼添加-fno-objc-arc選項讓項目支援ARC。
接下來,将所需要的資源檔案,包括圖檔和聲音拖到項目的“Resources”組。在遊戲開發當中,通常都會使用精靈表單來優化遊戲性能,在這個小遊戲當中,雖然這種性能優化并不會有特别明顯的效果,但是建議大家以後開發遊戲時都使用精靈表單來提高遊戲性能。使用Zwoptex将所有圖檔制作成精靈表單,生成對應的airfightSheet.png和airfightSheet.plist檔案,并将這兩個檔案拖到項目的“Resources”組。
現在,我們來為遊戲添加一個菜單設定功能,在這裡可以完成開始遊戲、遊戲設定、退出遊戲等操作。步驟如下。
①選擇“AirfightGame”組并單擊右鍵,選擇“New File”,在左邊欄中選擇“cocos2d v2.x”模闆,在右邊的模闆類中選擇“CCNode class”模闆類,“Subclass of”選擇“CCLayer”,然後單擊“Next”按鈕。命名為“MenuLayer”,然後單擊“Create”按鈕。
MenuLayer繼承自CCLayer,提供一個類方法scene供CCDirector對象調用。該類的作用是顯示一個菜單場景,讓使用者選擇。
打開MenuLayer.m檔案,實作代碼如下。
程式清單:codes/13/13.14/AirfightGame/AirfightGame/MenuLayer.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<code>-(id) init</code>
<code>{</code>
<code> </code><code>if</code><code>( (self=[super init]) ) {</code>
<code> </code><code>CGSize winSize = [[CCDirector sharedDirector] winSize];</code>
<code> </code><code>// 建立“開始遊戲”标簽,當觸碰該标簽時,調用startGame:方法</code>
<code> </code><code>CCMenuItemFont* startItem = [CCMenuItemFont itemWithString:@</code><code>"開始遊戲"</code>
<code> </code><code>target:self selector:@selector(startGame:)];</code>
<code> </code><code>startItem.position=ccp(winSize.width/2, winSize.height*0.6);</code>
<code> </code><code>// 建立“遊戲設定”标簽,當觸碰該标簽時,調用setting:方法</code>
<code> </code><code>CCMenuItemFont* settingItem = [CCMenuItemFont itemWithString:@</code><code>"遊戲設定"</code>
<code> </code><code>target:self selector:@selector(setting:)];</code>
<code> </code><code>// 設定“遊戲設定”标簽位置</code>
<code> </code><code>settingItem.position=ccp(winSize.width/2, winSize.height*0.4);</code>
<code> </code><code>// 建立控制菜單,并将兩個标簽添加進去</code>
<code> </code><code>CCMenu* menu = [CCMenu menuWithItems:startItem,settingItem, nil];</code>
<code> </code><code>menu.position = CGPointZero;</code>
<code> </code><code>[self addChild:menu];</code>
<code> </code><code>}</code>
<code> </code><code>return</code> <code>self;</code>
<code>}</code>
init方法比較簡單,建立了兩個CCMenuItemFont,選擇标簽時會調用對應的startGame:和setting:方法,并将它們添加到CCMenu當中,再将CCMenu添加為目前層的子節點。
②添加startGame:和setting:兩個方法,實作代碼如下(程式清單同上):
<code>-(</code><code>void</code><code>) startGame:(id)sender{</code>
<code> </code><code>// 切換到PreloadLayer場景</code>
<code> </code><code>CCTransitionSlideInL* transitionScene = [CCTransitionSlideInL</code>
<code> </code><code>transitionWithDuration:2.0 scene:[PreloadLayer scene]];</code>
<code> </code><code>[[CCDirector sharedDirector] replaceScene:transitionScene];</code>
startGame:方法非常簡單,當使用者選擇“開始遊戲”标簽時,場景切換到PreloadLayer,在下一節中将重點介紹PreloadLayer(程式清單同上)。
<code>-(</code><code>void</code><code>) setting:(id)sender{</code>
<code> </code><code>// 切換到SettingLayer場景</code>
<code> </code><code>transitionWithDuration:2.0 scene:[SettingLayer scene]];</code>
當使用者選擇“遊戲設定”标簽時,場景切換到SettingLayer,進行遊戲設定。
③同上面的步驟一樣,使用“cocos2d v2.x”模闆建立一個類并命名為“SettingLayer”,繼承自CCLayer。該類的實作代碼如下。
程式清單:codes/13/13.14/AirfightGame/AirfightGame/SettingLayer.m
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<code> </code><code>// 提示菜單項</code>
<code> </code><code>CCMenuItemFont* musicItem = [CCMenuItemFont itemWithString:@</code><code>"背景音樂:"</code><code>];</code>
<code> </code><code>musicItem.position = ccp(winSize.width*0.4, winSize.height*0.6);</code>
<code> </code><code>// 建立“開”和“關”菜單項</code>
<code> </code><code>CCMenuItemFont* musicOn = [CCMenuItemFont itemWithString:@</code><code>"開"</code><code>];</code>
<code> </code><code>CCMenuItemFont* musicOff = [CCMenuItemFont itemWithString:@</code><code>"關"</code><code>];</code>
<code> </code><code>// CCMenuItemToggle,預設顯示“開”。開=0,關=1</code>
<code> </code><code>CCMenuItemToggle* musicToggle = [CCMenuItemToggle itemWithTarget:self</code>
<code> </code><code>selector:@selector(change:) items:musicOff,musicOn, nil];</code>
<code> </code><code>musicToggle.position = ccp(winSize.width*0.6, winSize.height*0.6);</code>
<code> </code><code>// 建立“傳回主菜單“菜單項</code>
<code> </code><code>CCMenuItemFont* returnItem = [CCMenuItemFont itemWithString:@</code><code>"傳回主菜單"</code>
<code> </code><code>target:self selector:@selector(backToMainLayer:)];</code>
<code> </code><code>returnItem.position = ccp(winSize.width/2, winSize.height*0.4);</code>
<code> </code><code>// 建立控制菜單,并将3個标簽添加進去</code>
<code> </code><code>CCMenu* menu = [CCMenu menuWithItems:musicItem,musicToggle,returnItem, nil];</code>
<code> </code><code>// NSUserDefaults使用者首選項可以用來儲存使用者在操作應用的過程中設定的首選項。</code>
<code> </code><code>NSUserDefaults* userDef = [NSUserDefaults standardUserDefaults];</code>
<code> </code><code>// 如果Bool為No,則顯示1=關</code>
<code> </code><code>if</code><code>(![userDef boolForKey:@</code><code>"music"</code><code>]){</code>
<code> </code><code>musicToggle.selectedIndex = 1;</code>
<code> </code><code>}</code>
<code>-(</code><code>void</code><code>) change:(id)sender{</code>
<code> </code><code>// 判斷mute(靜音)屬性,根據屬性狀态進行切換</code>
<code> </code><code>if</code><code>([CDAudioManager sharedManager].mute == TRUE){</code>
<code> </code><code>[CDAudioManager sharedManager].mute = FALSE;</code>
<code> </code><code>}</code><code>else</code><code>{</code>
<code> </code><code>[CDAudioManager sharedManager].mute = TRUE;</code>
<code> </code><code>NSUserDefaults* userDef = [NSUserDefaults standardUserDefaults];</code>
<code> </code><code>CCMenuItemToggle* tooggle = (CCMenuItemToggle*)sender;</code>
<code> </code><code>// 關=1,設定Bool為NO</code>
<code> </code><code>if</code><code>(tooggle.selectedIndex == 1){</code>
<code> </code><code>[userDef setBool:NO forKey:@</code><code>"music"</code><code>];</code>
<code> </code><code>[userDef setBool:YES forKey:@</code><code>"music"</code><code>];</code>
<code>// 定義一個CCTransitionSlideInL場景切換效果,并使用CCDirector單例對象來切換場景</code>
<code>-(</code><code>void</code><code>) backToMainLayer:(id)sender{</code>
<code> </code><code>CCTransitionSlideInL* transitionScene =</code>
<code> </code><code>[CCTransitionSlideInL transitionWithDuration:2.0 scene:[MenuLayer scene]];</code>
SettingLayer類代碼在13.13.2節中已經詳細介紹過,這裡不再贅述。
④修改IntroLayer.m檔案
IntroLayer預設加載HelloWorldLayer,但此時我們不再使用HelloWorldLayer作為應用的第一個場景,而是使用MenuLayer作為應用的第一個場景,是以需要修改IntroLayer,将IntroLayer改為加載MenuLayer場景。修改如下。
在IntroLayer.m檔案的頂部添加所包含的頭檔案:
<code>#import "MenuLayer.h"</code>
修改-(void) makeTransition:(ccTime)dt方法,将該方法改成下面的代碼。
程式清單:codes/13/13.14/AirfightGame/AirfightGame/IntroLayer.m
<code>-(</code><code>void</code><code>) makeTransition:(ccTime)dt</code>
<code> </code><code>[[CCDirector sharedDirector] replaceScene:</code>
<code> </code><code>[CCTransitionFade transitionWithDuration:1.0</code>
<code> </code><code>scene:[MenuLayer scene] withColor:ccWHITE]];</code>
<code>01</code>
<code>02</code>
<code>03</code>
<code></code><code>if</code><code>( (self=[super init]) ) {</code>
<code>04</code>
<code></code><code>CGSize winSize = [[CCDirector sharedDirector] winSize];</code>
<code>05</code>
<code></code><code>// 建立“開始遊戲”标簽,當觸碰該标簽時,調用startGame:方法</code>
<code>06</code>
<code></code><code>CCMenuItemFont* startItem = [CCMenuItemFont itemWithString:@</code><code>"開始遊戲"</code>
<code>07</code>
<code></code><code>target:self selector:@selector(startGame:)];</code>
<code>08</code>
<code></code><code>startItem.position=ccp(winSize.width/2, winSize.height*0.6);</code>
<code>09</code>
<code></code><code>// 建立“遊戲設定”标簽,當觸碰該标簽時,調用setting:方法</code>
<code>10</code>
<code></code><code>CCMenuItemFont* settingItem = [CCMenuItemFont itemWithString:@</code><code>"遊戲設定"</code>
<code>11</code>
<code></code><code>target:self selector:@selector(setting:)];</code>
<code>12</code>
<code></code><code>// 設定“遊戲設定”标簽位置</code>
<code>13</code>
<code></code><code>settingItem.position=ccp(winSize.width/2, winSize.height*0.4);</code>
<code>14</code>
<code></code><code>// 建立控制菜單,并将兩個标簽添加進去</code>
<code>15</code>
<code></code><code>CCMenu* menu = [CCMenu menuWithItems:startItem,settingItem, nil];</code>
<code>16</code>
<code></code><code>menu.position = CGPointZero;</code>
<code>17</code>
<code></code><code>[self addChild:menu];</code>
<code>18</code>
<code></code><code>}</code>
<code>19</code>
<code></code><code>return</code><code>self;</code>
<code>20</code>
<code>1</code>
<code>2</code>
<code></code><code>// 切換到PreloadLayer場景</code>
<code>3</code>
<code></code><code>CCTransitionSlideInL* transitionScene = [CCTransitionSlideInL</code>
<code>4</code>
<code></code><code>transitionWithDuration:2.0 scene:[PreloadLayer scene]];</code>
<code>5</code>
<code></code><code>[[CCDirector sharedDirector] replaceScene:transitionScene];</code>
<code>6</code>
<code></code><code>// 切換到SettingLayer場景</code>
<code></code><code>transitionWithDuration:2.0 scene:[SettingLayer scene]];</code>
<code></code><code>// 提示菜單項</code>
<code></code><code>CCMenuItemFont* musicItem = [CCMenuItemFont itemWithString:@</code><code>"背景音樂:"</code><code>];</code>
<code></code><code>musicItem.position = ccp(winSize.width*0.4, winSize.height*0.6);</code>
<code></code><code>// 建立“開”和“關”菜單項</code>
<code></code><code>CCMenuItemFont* musicOn = [CCMenuItemFont itemWithString:@</code><code>"開"</code><code>];</code>
<code></code><code>CCMenuItemFont* musicOff = [CCMenuItemFont itemWithString:@</code><code>"關"</code><code>];</code>
<code></code><code>// CCMenuItemToggle,預設顯示“開”。開=0,關=1</code>
<code></code><code>CCMenuItemToggle* musicToggle = [CCMenuItemToggle itemWithTarget:self</code>
<code></code><code>selector:@selector(change:) items:musicOff,musicOn, nil];</code>
<code></code><code>musicToggle.position = ccp(winSize.width*0.6, winSize.height*0.6);</code>
<code></code><code>// 建立“傳回主菜單“菜單項</code>
<code></code><code>CCMenuItemFont* returnItem = [CCMenuItemFont itemWithString:@</code><code>"傳回主菜單"</code>
<code></code><code>target:self selector:@selector(backToMainLayer:)];</code>
<code></code><code>returnItem.position = ccp(winSize.width/2, winSize.height*0.4);</code>
<code></code><code>// 建立控制菜單,并将3個标簽添加進去</code>
<code></code><code>CCMenu* menu = [CCMenu menuWithItems:musicItem,musicToggle,returnItem, nil];</code>
<code>21</code>
<code>22</code>
<code>23</code>
<code></code><code>// NSUserDefaults使用者首選項可以用來儲存使用者在操作應用的過程中設定的首選項。</code>
<code>24</code>
<code></code><code>NSUserDefaults* userDef = [NSUserDefaults standardUserDefaults];</code>
<code>25</code>
<code></code><code>// 如果Bool為No,則顯示1=關</code>
<code>26</code>
<code></code><code>if</code><code>(![userDef boolForKey:@</code><code>"music"</code><code>]){</code>
<code>27</code>
<code></code><code>musicToggle.selectedIndex = 1;</code>
<code>28</code>
<code>29</code>
<code>30</code>
<code>31</code>
<code>32</code>
<code>33</code>
<code></code><code>// 判斷mute(靜音)屬性,根據屬性狀态進行切換</code>
<code>34</code>
<code></code><code>if</code><code>([CDAudioManager sharedManager].mute == TRUE){</code>
<code>35</code>
<code></code><code>[CDAudioManager sharedManager].mute = FALSE;</code>
<code>36</code>
<code></code><code>}</code><code>else</code><code>{</code>
<code>37</code>
<code></code><code>[CDAudioManager sharedManager].mute = TRUE;</code>
<code>38</code>
<code>39</code>
<code>40</code>
<code></code><code>CCMenuItemToggle* tooggle = (CCMenuItemToggle*)sender;</code>
<code>41</code>
<code></code><code>// 關=1,設定Bool為NO</code>
<code>42</code>
<code></code><code>if</code><code>(tooggle.selectedIndex == 1){</code>
<code>43</code>
<code></code><code>[userDef setBool:NO forKey:@</code><code>"music"</code><code>];</code>
<code>44</code>
<code>45</code>
<code></code><code>[userDef setBool:YES forKey:@</code><code>"music"</code><code>];</code>
<code>46</code>
<code>47</code>
<code>48</code>
<code>49</code>
<code>50</code>
<code></code><code>CCTransitionSlideInL* transitionScene =</code>
<code>51</code>
<code></code><code>[CCTransitionSlideInL transitionWithDuration:2.0 scene:[MenuLayer scene]];</code>
<code>52</code>
<code>53</code>
<code></code><code>[[CCDirector sharedDirector] replaceScene:</code>
<code></code><code>[CCTransitionFade transitionWithDuration:1.0</code>
<code></code><code>scene:[MenuLayer scene] withColor:ccWHITE]];</code>
編譯并運作遊戲,運作時模拟器顯示效果如圖13.58所示。

在真實項目當中,在遊戲開始前,都會預先加載遊戲所需要的圖檔、背景音樂、音效等資源,這裡介紹如何制作一個PreloadLayer來預加載遊戲資源。
選擇“AirfightGame”組并單擊右鍵,選擇“New File”,在左邊欄中選擇“cocos2d v2.x”模闆,在右邊的模闆類中選擇“CCNode class”模闆類,“Subclass of”選擇“CCLayer”,然後單擊“Next”按鈕。命名為“PreloadLayer”,然後單擊“Create”按鈕。
PreloadLayer繼承自CCLayer,提供一個類方法scene供CCDirector對象調用。該類的作用是預加載遊戲資源,在加載過程中會顯示一個進度條,進度條全部顯示完成代表加載完畢,加載完畢後顯示遊戲主場景。
首先打開PreloadLayer.m檔案,先在檔案上方定義一個私有的Category。實作代碼如下。
程式清單:codes/13/13.14/AirfightGame/AirfightGame/PreloadLayer.m
<code>/**</code>
<code> </code><code>定義一個私有的Category,為了不讓API暴露給用戶端</code>
<code> </code><code>将一些類内部所使用的方法和變量放在私有的擴充裡面,而不是直接聲明在頭檔案當中</code>
<code> </code><code>*/</code>
<code>@interface PreloadLayer ()</code>
<code>- (</code><code>void</code><code>) loadMusics:(NSArray *) musicFiles; </code><code>// 加載背景音樂</code>
<code>- (</code><code>void</code><code>) loadSounds:(NSArray *) soundClips; </code><code>// 加載遊戲音效</code>
<code>- (</code><code>void</code><code>) loadSpriteSheets:(NSArray *) spriteSheets; </code><code>// 加載精靈表單</code>
<code>- (</code><code>void</code><code>) loadingComplete; </code><code>// 資源全部加載完成,切換到另一個遊戲場景</code>
<code>- (</code><code>void</code><code>) progressUpdate; </code><code>// 更新遊戲進度條,計算何時加載完成</code>
<code>@end;</code>
這裡定義了一系列的load方法,每個方法接收一個NSArray數組作為參數。這些參數代表一些具體資源的檔案名,參數值會從一個配置檔案中讀取出來,該配置檔案在之後的代碼實作時會給出。
然後定義3個變量,其中sourceCount用來儲存遊戲需要加載的資源總數;progress用于顯示進度條,CCProgressTimer 類是cocos2d中對進度條的一個封裝,用來實作各種進度條功能,非常友善,之後我們還會使用該類來實作遊戲的自定義血條量;progressInterval代表進度條更新的次數。實作代碼如下:
<code>@implementation PreloadLayer</code>
<code>// 用來儲存遊戲需要加載的資源總數</code>
<code>int</code> <code>sourceCount;</code>
<code>// 顯示進度條的成員變量</code>
<code>CCProgressTimer* progress;</code>
<code>// 代表進度條更新的次數</code>
<code>float</code> <code>progressInterval;</code>
建立一個preloadResources.plist檔案,該檔案用于儲存項目需要的所有資源檔案,檔案内容如圖13.59所示。
可以看出,加載的音效是b0.mp3,精靈表單是airfightSheet.plist,背景音樂是s3.wav。
在PreloadLayer.m檔案中添加代碼。實作代碼如下。
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<code>+ (CCScene *) scene</code>
<code> </code><code>CCScene* scene = [CCScene node];</code>
<code> </code><code>PreloadLayer* layer = [PreloadLayer node];</code>
<code> </code><code>[scene addChild:layer];</code>
<code> </code><code>return</code> <code>scene;</code>
<code>- (id) init{</code>
<code> </code><code>if</code><code>((self = [super init])){</code>
<code> </code><code>// 擷取螢幕大小</code>
<code> </code><code>// 建立一個進度條精靈</code>
<code> </code><code>CCSprite* barSprite = [CCSprite spriteWithFile:@</code><code>"progressbar.png"</code><code>];</code>
<code> </code><code>// 初始化一個CCProgressTimer進度條對象</code>
<code> </code><code>progress = [CCProgressTimer progressWithSprite:barSprite];</code>
<code> </code><code>// setPercentage:0.0f,表示并未加載任何資源,表現在螢幕上就是什麼也看不見</code>
<code> </code><code>[progress setPercentage:0.0f];</code>
<code> </code><code>// 由于圖檔大小關系,把scale設定成0.5,即縮小一半</code>
<code> </code><code>progress.scale = 0.5;</code>
<code> </code><code>// 設定進度條動畫的起始位置,預設在圖檔的中點</code>
<code> </code><code>// 如果想要顯示從左到右的一個動畫效果,必須改成(0,y)</code>
<code> </code><code>progress.midpoint = ccp(0,0.5);</code>
<code> </code><code>// barChangeRate表示是否改變水準或者垂直方向的比例,設定成1表示改變,0表示不改變</code>
<code> </code><code>progress.barChangeRate = ccp(1,0);</code>
<code> </code><code>// 本例制作一個從左至右的水準進度條,是以midpoint應該是(0,0.5)</code>
<code> </code><code>// 因為x方向需要改變,而y方向不需要改變,是以barChangeRate = ccp(1, 0)</code>
<code> </code><code>// kCCProgressTimerTypeBar表示為條形進度條</code>
<code> </code><code>progress.type = kCCProgressTimerTypeBar;</code>
<code> </code><code>// 設定position在中心點</code>
<code> </code><code>[progress setPosition:ccp(winSize.width/2,winSize.height/2)];</code>
<code> </code><code>// 将進度條添加為目前層的子節點</code>
<code> </code><code>[self addChild:progress];</code>
<code>- (</code><code>void</code><code>) onEnterTransitionDidFinish{</code>
<code> </code><code>[super onEnterTransitionDidFinish];</code>
<code> </code><code>// 加載preloadResources.plist配置檔案</code>
<code> </code><code>NSString* path = [[CCFileUtils sharedFileUtils]</code>
<code> </code><code>fullPathFromRelativePath:@</code><code>"preloadResources.plist"</code><code>];</code>
<code> </code><code>// 讀取配置檔案中的遊戲資源名稱清單,傳回一個NSDictionary對象</code>
<code> </code><code>NSDictionary* resources = [NSDictionary dictionaryWithContentsOfFile:path];</code>
<code> </code><code>// 通過key值取出每種不同類型資源的數組</code>
<code> </code><code>NSArray *spriteSheets = [resources objectForKey:@</code><code>"SpriteSheets"</code><code>];</code>
<code> </code><code>NSArray *sounds = [resources objectForKey:@</code><code>"Sounds"</code><code>];</code>
<code> </code><code>NSArray *musics = [resources objectForKey:@</code><code>"Musics"</code><code>];</code>
<code> </code><code>// 調用數組的count方法得到總共需要加載的資源數量</code>
<code> </code><code>sourceCount = [spriteSheets count] + [sounds count] + [musics count];</code>
<code> </code><code>// 設定進度條更新次數=100/需要加載的資源數量</code>
<code> </code><code>progressInterval = 100.0 / (</code><code>float</code><code>) sourceCount;</code>
<code> </code><code>// 調用performSelectorOnMainThread在主線程上依次加載每種類型的遊戲資源</code>
<code> </code><code>// waitUntilDone的值為YES能保證所有的資源按照序列依次加載</code>
<code> </code><code>if</code><code>(sounds){</code>
<code> </code><code>[self performSelectorOnMainThread:@selector(loadSounds:)</code>
<code> </code><code>withObject:sounds waitUntilDone:YES];</code>
<code> </code><code>if</code><code>(spriteSheets){</code>
<code> </code><code>[self performSelectorOnMainThread:@selector(loadSpriteSheets:)</code>
<code> </code><code>withObject:spriteSheets waitUntilDone:YES];</code>
<code> </code><code>if</code><code>(musics){</code>
<code> </code><code>[self performSelectorOnMainThread:@selector(loadMusic:)</code>
<code> </code><code>withObject:musics waitUntilDone:YES];</code>
<code>// 加載背景音樂</code>
<code>- (</code><code>void</code><code>) loadMusics:(NSArray *)musicFiles{</code>
<code> </code><code>for</code> <code>(NSString *music in musicFiles) {</code>
<code> </code><code>[[SimpleAudioEngine sharedEngine] preloadBackgroundMusic:music];</code>
<code> </code><code>[self progressUpdate];</code>
<code>// 加載聲音</code>
<code>- (</code><code>void</code><code>) loadSounds:(NSArray *)soundClips{</code>
<code> </code><code>for</code> <code>(NSString *soundClip in soundClips) {</code>
<code> </code><code>[[SimpleAudioEngine sharedEngine] preloadEffect:soundClip];</code>
<code>// 加載精靈表單</code>
<code>- (</code><code>void</code><code>) loadSpriteSheets:(NSArray *)spriteSheets{</code>
<code> </code><code>for</code> <code>(NSString *spriteSheet in spriteSheets) {</code>
<code> </code><code>// 該方法會加載與該plist檔案名稱相同但字尾為.png的紋理圖檔</code>
<code> </code><code>// 把該plist的所有spriteFrame資訊讀取出來</code>
<code> </code><code>// 在之後的代碼中可以通過spriteFrameWithName擷取相應的精靈幀</code>
<code> </code><code>// 本例中airfightSheet.plist對應airfightSheet.png</code>
<code> </code><code>[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: spriteSheet];</code>
<code>- (</code><code>void</code><code>) progressUpdate{</code>
<code> </code><code>// 每次調用該方法說明加載一個資源,自減更新資源總數</code>
<code> </code><code>if</code> <code>(--sourceCount) {</code>
<code> </code><code>[progress setPercentage:100.0f-(progressInterval * sourceCount)];</code>
<code> </code><code>// CCProgressFromTo動作用于以漸進的方式顯示圖檔</code>
<code> </code><code>// actionWithDuration表示持續0.5秒,from表示進度條百分百從開始一直到100</code>
<code> </code><code>CCProgressFromTo *ac = [CCProgressFromTo actionWithDuration:0.5</code>
<code> </code><code>from:progress.percentage to:100];</code>
<code> </code><code>// 當資源全部加載完畢時調用loadingComplete方法</code>
<code> </code><code>CCCallBlock *callback = [CCCallBlock actionWithBlock:^() {</code>
<code> </code><code>[self loadingComplete];</code>
<code> </code><code>}];</code>
<code> </code><code>// CCSequence組合動作</code>
<code> </code><code>id action = [CCSequence actions:ac.callback.nil];</code>
<code> </code><code>// 進度條執行動作</code>
<code> </code><code>[progress runAction:action];</code>
<code>// 延遲2秒之後運作一個場景切換特效跳轉到遊戲主場景,即HelloWorldLayer</code>
<code>- (</code><code>void</code><code>) loadingComplete{</code>
<code> </code><code>CCDelayTime *delay = [CCDelayTime actionWithDuration:2.0f];</code>
<code> </code><code>CCCallBlock *callblock = [CCCallBlock actionWithBlock:^(</code><code>void</code><code>) {</code>
<code> </code><code>[[CCDirector sharedDirector] replaceScene:</code>
<code> </code><code>[CCTransitionFade transitionWithDuration:1.0f scene:[HelloWorldLayer scene]]];</code>
<code> </code><code>}];</code>
<code> </code><code>CCSequence *sequence = [CCSequence actions:delay.callblock.nil];</code>
<code> </code><code>[self runAction:sequence];</code>
<code>@end</code>
下面依次解釋以上代碼中的每一個方法。
+(CCScene *) scene方法很簡單,和前面的一樣,首先建立了一個scene場景,然後建立了一個PreloadLayer層,将PreloadLayer層作為scene場景的子節點,最後傳回scene場景。
init方法首先擷取螢幕視窗大小,然後建立了一個進度條。這裡使用progressbar.png圖檔初始化一個精靈,再通過該精靈初始化一個CCProgressTimer對象,設定setPercentage屬性為0,表示目前未加載任何資源,表現在螢幕上就是什麼也看不見。由于圖檔大小關系,把scale設定成0.5,即縮小一半。
接下來設定CCProgressTimer對象最重要的3個參數。
qmidpoint:表示進度條動畫的起始位置,預設在圖檔的中點,如果想要顯示從左到右的一個動畫效果,則必須改成(0,y)。
qbarChangeRate:表示是否改變水準或者垂直方向的比例,設定成1表示改變,0表示不改變。本例制作一個從左至右的水準進度條,是以midpoint應該是(0,0.5)。因為x方向需要改變,而y方向不需要改變,是以設定barChangeRate為ccp(1,0)。
qtype:設定為kCCProgressTimerTypeBar,表示條形進度條。
關于CCProgressTimer類的使用可以參考官方文檔,讀者也可以找到cocos2d的示例項目cocos2d-tests-ios.xcodeproj,并運作ActionProgressTest這個TARGET。感興趣的讀者也可以仔細分析該項目中的ActionProgressTest.m源檔案(位于項目的tests目錄下)來掌握不同progress的用法示例。
最後設定CCProgressTimer對象的位置,并添加為目前層的子節點。
onEnterTransitionDidFinish方法加載配置檔案preloadResources.plist,讀取配置檔案中的遊戲資源名稱清單并存儲在不同的數組中。首先使用CCFileUtil獲得plist檔案的具體路徑,調用NSDictionary的dictionaryWithContentsOfFile方法把該檔案轉換成一個字典對象。然後通過字典的key值取出不同類型資源的數組,調用每個數組的count方法累加得到總共需要加載的資源總數量,使用100除以資源總數量獲得進度條需要更新次數用于之後計算進度條顯示的百分比。最後将數組作為參數調用performSelectorOnMainThread: withObject: waitUntilDone:方法在主線程中依次加載每種類型的遊戲資源,将waitUntilDone的值設定為“YES”能保證所有的資源按照序列依次加載。
loadMusics:和loadSounds:方法比較簡單,通過循環周遊數組,預加載背景音樂和音效,加載完後調用progressUpdate方法更新進度條。
這裡需要注意的是loadSpriteSheets:方法,該方法循環周遊數組,數組的每個元素是一個plist檔案名稱,調用CCSpriteFrameCache的addSpriteFramesWithFile時,該方法會加載與該plist檔案名稱相同但字尾為.png的紋理圖檔(本例中airfightSheet.plist對應airfightSheet.png),把該plist的所有spriteFrame資訊讀取出來,之後在項目當中就可以通過spriteFrameWithName擷取相應的精靈幀了。
progressUpdate方法比較簡單,每次被調用時自減更新資源總數變量,修改進度條的百分比。當資源全部加載完畢時調用loadingComplete方法。
loadingComplete方法被調用說明資源加載完畢,延遲2秒後運作一個場景切換特效跳轉到遊戲主場景HelloWorldLayer。
編譯并運作遊戲,選擇“開始遊戲”菜單項,模拟器首先會顯示一個進度條,進度條全部顯示完畢切換到HelloWorldLayer顯示經典的Hello World畫面。恭喜你!資源檔案加載成功,進度條功能實作。運作時模拟器顯示效果如圖13.60所示。