日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

iOS cocos2d 2游戏开发实战(第3版)---你的第一个游戏!

發(fā)布時(shí)間:2025/3/20 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS cocos2d 2游戏开发实战(第3版)---你的第一个游戏! 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

隨著蘋果公司不斷地創(chuàng)新與發(fā)展,新的iPhone 5、iPad 4以及iPad mini產(chǎn)品相繼問(wèn)世,包括iOS與Xcode在內(nèi)的開(kāi)發(fā)環(huán)境和開(kāi)發(fā)工具也都有了更新和進(jìn)步。相信有不少開(kāi)發(fā)團(tuán)隊(duì)正緊鑼密鼓地在iPhone 5和iPad mini上部署自己的應(yīng)用,一切都是那么令人激動(dòng)!而隨著iOS 6的推出,cocos2d游戲引擎又有了新的發(fā)展。在IT行業(yè),可謂唯一不變的便是變化。日新月異的技術(shù)需要同為開(kāi)發(fā)者的你我保持強(qiáng)盛的好奇心和完美的學(xué)習(xí)狀態(tài),只有這樣,才能不斷進(jìn)步,開(kāi)發(fā)出更酷的游戲或應(yīng)用。相信這本書(shū)的再版,一定能使你受益匪淺,幫助你實(shí)現(xiàn)夢(mèng)想。


你的第一個(gè)游戲??? 第四章

在本章中,你將編寫專屬于你的第一個(gè)完整的游戲。它不會(huì)為你贏得什么獎(jiǎng)項(xiàng),但從中可以學(xué)到cocos2d基本要素的使用方法,而且這個(gè)游戲是很容易修改的。實(shí)際上,本書(shū)之前版本的讀者已經(jīng)制作了這個(gè)游戲的幾個(gè)修改版并發(fā)布到了App Store中。
這個(gè)游戲是著名的“Doodle Jump”游戲的“倒版”,它被貼切地命名為“DoodleDrop”。玩家通過(guò)旋轉(zhuǎn)屏幕盡可能躲避落下的障礙物。游戲的最終版本如圖4-1所示,你可以在此提前了解一下將在本章創(chuàng)造的作品。

圖 4-1? “DoodleDrop”游戲的最終版本

4.1? 創(chuàng)建DoodleDrop項(xiàng)目
在第2章我們學(xué)習(xí)了如何創(chuàng)建支持ARC的Kobold2D和cocos2d項(xiàng)目。
Kobold2D從運(yùn)行Kobold2D啟動(dòng)應(yīng)用程序開(kāi)始,然后選擇Empty-Project 模板(見(jiàn)第2章中的圖2-2)。使用DoodleDrop作為項(xiàng)目名稱,基本工作就完成了。剩下的事就是選擇應(yīng)用程序的目標(biāo)程序,選擇Supported Device Orientation的portrait mode圖標(biāo)(見(jiàn)第3章中的圖3-17)。由于DoodleDrop是設(shè)計(jì)在Portrait模式下玩的,因此不選擇landscape mode圖標(biāo)。
下面的部分僅適于cocos2d用戶——Kobold2D用戶可以略過(guò)。


4.2? 從一個(gè)支持ARC的cocos2d項(xiàng)目開(kāi)始
cocos2d用戶要?jiǎng)?chuàng)建支持ARC的cocos2d項(xiàng)目,就應(yīng)該依照第2章中的指導(dǎo)內(nèi)容來(lái)做。如果已經(jīng)做過(guò)了,就只要把已經(jīng)創(chuàng)建的項(xiàng)目復(fù)制一遍。這樣做可以節(jié)省時(shí)間:保留一份由原始cocos2d項(xiàng)目模板轉(zhuǎn)換得來(lái)的支持ARC的未修改版本,這樣每次創(chuàng)建新項(xiàng)目時(shí)就會(huì)比較簡(jiǎn)單快捷。
提示:
在這本書(shū)的源代碼中,可以在Cocos2D_ARC_Template_Projects文件夾下找到支持ARC的cocos2d模板項(xiàng)目。
在完成第2章中的指導(dǎo)內(nèi)容之后,就有一個(gè)支持ARC的cocos2d項(xiàng)目了。我的項(xiàng)目取名為cocos2d-2.x-ARC-iOS。用Xcode打開(kāi)之前先簡(jiǎn)單地將包含.xcodeproj文件的文件夾拷貝一份。但是不要自己重命名.xcodeproj文件,因?yàn)槟菢幼鰰?huì)導(dǎo)致文件不可用。
現(xiàn)在你可以通過(guò)Xcode來(lái)重命名項(xiàng)目,這同樣也會(huì)重命名.xcodeproj文件。在Project Navigator中選擇cocos2d-2.x-ARC-iOS項(xiàng)目(第一個(gè)條目,見(jiàn)第2章中的圖2-5),帶延遲地雙擊來(lái)編輯該條目。就是單擊一次,停頓兩秒,再單擊一次。這樣項(xiàng)目名稱就可以編輯了。輸入DoodleDrop作為這個(gè)項(xiàng)目的名稱。
按下Enter鍵確認(rèn)修改之后,Xcode會(huì)問(wèn)你是否確認(rèn)重命名圖4-2所示的那幾項(xiàng)。確認(rèn)則單擊Rename。否則Xcode 仍然會(huì)重命名項(xiàng)目,但是其他名稱不變。所以就算你這個(gè)時(shí)候發(fā)現(xiàn)了拼寫錯(cuò)誤,或者突然不喜歡這個(gè)名字了,也都應(yīng)該單擊Rename。重命名之后考慮到Prefix.pch文件,可能還會(huì)出現(xiàn)警告:“New name for file can not be the same”。無(wú)視這個(gè)警告即可,因?yàn)椴粫?huì)有什么問(wèn)題。
還有最后一個(gè)需要手動(dòng)重命名的條目:應(yīng)用程序的計(jì)劃方案。應(yīng)該仍然名為cocos2d- 2.x-ARC-iOS,也就是你命名項(xiàng)目時(shí)用的那個(gè)名稱。選擇Product Manage Schemes來(lái)查看方案列表。延遲雙擊方案名稱來(lái)選擇并編輯,重命名為DoodleDrop。完成之后,關(guān)閉scheme列表。

圖4-2? 確認(rèn)重命名項(xiàng)目以及相關(guān)文件
由于DoodleDrop是Portrait模式應(yīng)用程序,因此需要編輯AppDelegate.m 文件。修改shouldAutorotateToInterfaceOrientation方法,使之返回YES以僅用于Portrait模式:
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
現(xiàn)在,在Run和Stop按鍵右邊的下拉菜單中選擇DoodleDrop的項(xiàng)目方案,然后運(yùn)行,確認(rèn)一切工作正常,如圖4-3所示。

圖4-3? 開(kāi)始游戲吧!DoodleDrop項(xiàng)目基于第2章中的cocos2d ARC
???? 項(xiàng)目,Kobold2D的Empty-Project模板也不會(huì)有太大區(qū)別
4.3? 創(chuàng)建DoodleDrop場(chǎng)景
下一步你要作如下決定:是使用已有的HelloWorldLayer作為起點(diǎn),之后再把名字改成現(xiàn)在的項(xiàng)目名稱呢?還是創(chuàng)建自己的場(chǎng)景?是我就選擇后者。因?yàn)槟氵t早需要添加新的場(chǎng)景,所以還不如現(xiàn)在就學(xué)習(xí)如何從頭創(chuàng)建新的場(chǎng)景。
請(qǐng)確定已選擇你準(zhǔn)備添加新場(chǎng)景類的組,然后選擇File | New | New File或者右擊Project Navigator樹(shù)中的合適位置,在彈出的菜單中選擇New File,打開(kāi)如圖4-4所示的New File 對(duì)話框。

圖4-4? 添加新的CCNode派生類的最好方式是通過(guò)使用cocos2d或Kobold2D提供的類模板。
????? 在這個(gè)例子中,因?yàn)槲覀円獎(jiǎng)?chuàng)建新的場(chǎng)景,所以選擇的CCNode類是CCLayer的子類
cocos2d和Kobold2D為大多數(shù)重要的節(jié)點(diǎn)和類都提供了類模板,不使用它們太浪費(fèi)了!另外,Xcode自帶的Objective-C類也是很好的模板——只需要手動(dòng)將基類由NSObject 改為CCLayer即可。在cocos2d v2.x的模板部分選擇CCNode類,單擊Next按鈕,再次單擊Next 按鈕會(huì)彈出如圖4-5所示的Save File對(duì)話框,在此之前,確認(rèn)將該類設(shè)置為CCLayer 的子類。
我是將新建的文件命名為GamLayer.m。整個(gè)DoodleDrop游戲邏輯都在這個(gè)文件中實(shí)現(xiàn),所以這個(gè)名字還十分合理。確保DoodleDrop目標(biāo)程序復(fù)選框被選中(見(jiàn)圖4-5)。
注意:
不檢查目標(biāo)設(shè)置的話可能會(huì)導(dǎo)致文件沒(méi)有被添加到正確的目標(biāo)程序里。這會(huì)引起一系列問(wèn)題——其中編譯錯(cuò)誤和“file not found”錯(cuò)誤是常見(jiàn)的典型錯(cuò)誤。這種情況有時(shí)還會(huì)導(dǎo)致游戲運(yùn)行時(shí)崩潰。把文件放進(jìn)完全不需要這些文件的目標(biāo)程序里,只會(huì)浪費(fèi)空間。


圖4-5? 給新場(chǎng)景命名,并且確保它被添加到正確的目標(biāo)程序中
目前,我們的GameLayer類是空的,為了將它設(shè)置為場(chǎng)景,我們要做的第一件事是在里面添加+(id) scene方法。我們?cè)谶@里插入的代碼和第3章的基本上一樣,只是層的類名不同而已。幾乎在任何一個(gè)類中都需要-(id) init方法。添加-(void) dealloc方法也無(wú)傷大雅,要是能輸出對(duì)象被正確銷毀的日志就更好了。監(jiān)視dealloc方法能有效提前做出系統(tǒng)預(yù)警,防止內(nèi)存泄漏。
我也是一位很謹(jǐn)慎的程序員,決定將第3章中介紹的日志語(yǔ)句添加進(jìn)來(lái)。程序清單4-1是完成后的GameLayer.h,程序清單4-2是完成后的 GameLayer.m。
程序清單4-1? 帶場(chǎng)景方法的GameLayer.h
#import < Foundation/Foundation.h>
#import "cocos2d.h"

@interface GameLayer : CCLayer
{
}

+(id) scene;

@end
程序清單4-2? 帶場(chǎng)景方法以及一些標(biāo)準(zhǔn)方法的GameLayer.m
#import "GameLayer.h"
@implementation GameLayer
+(id) scene
{
CCScene *scene=[CCScene node];
CCLayer* layer=[GameLayer node];
[scene addChild:layer]; return scene;
}

-(id) init
{
if ((self=[super init]))
{
?????? CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
? }
??? return self;
}
-(void) dealloc
{
??? CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);
}
@end
現(xiàn)在可以安全地刪除HelloWorldLayer類。在彈出的對(duì)話框中選擇Move to Trash選項(xiàng),將文件徹底刪除。選擇HelloWorldLayer.h和HelloWorldLayer.m兩個(gè)文件,在頂部菜單中選擇Edit | Delete,或者右擊選中的文件,在彈出的菜單中選擇Delete選項(xiàng)。
Kobold2D用戶現(xiàn)在只需要打開(kāi)Resources組中的config.lua文件,修改FirstSceneClassName即可,如下所示:
FirstSceneClassName = "GameLayer",
但是在純cocos2d應(yīng)用程序中,必須修改AppDelegate.m,將文件中所有的HelloWorldLayer修改成GameLayer。程序清單4-3中已經(jīng)突出顯示了要對(duì)#import和pushScene語(yǔ)句進(jìn)行的必要修改。
程序清單4-3? 修改AppDelegate.m 文件,用GameLayer類代替HelloWorldLayer
// replace the line #import "HelloWorldLayer.h" with this one:

#import "GameLayer.h" - (BOOL)application:(UIApplication *)application??
??? didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
// replace HelloWorldLayer with GameLayer
??? [director_ pushScene:[GameLayer scene]];
}
編譯并運(yùn)行,應(yīng)該能看到空白場(chǎng)景。成功了!如果碰到什么問(wèn)題,請(qǐng)將你的項(xiàng)目與本書(shū)附帶的DoodleDrop01項(xiàng)目進(jìn)行比較。
提示:
應(yīng)用程序成功生成但是運(yùn)行失敗?別忘了,一個(gè)Xcode 項(xiàng)目中有多個(gè)目標(biāo)程序,甚至一個(gè)目標(biāo)程序也會(huì)有多個(gè)方案。檢查Xcode 工具欄中Run 和Stop 按鈕右邊的方案選擇/部署目標(biāo)程序下拉菜單(見(jiàn)圖2-6)。下拉菜單的左邊部分用于選擇當(dāng)前方案。要確保選擇的是DoodleDrop。大多其他方案,比如cocos2d-library是靜態(tài)庫(kù)。你可以生成靜態(tài)庫(kù),但是不能運(yùn)行它們。刪除、隱藏和選擇方案這些都只能由用戶自己設(shè)置完成。其他的問(wèn)題我都已經(jīng)為你解釋清楚了。
4.4? 添加Player Sprite
接下來(lái)將添加Player Sprite(玩家精靈)并使用加速計(jì)控制它們的動(dòng)作。要添加玩家圖像,請(qǐng)?jiān)赬code中選擇Resources組,然后選擇File | Add Files to“DoodleDrop”,或者右擊并從菜單中選擇Add Files to“DoodleDrop”來(lái)打開(kāi)選擇文件的對(duì)話框。如果不小心把文件添加到了錯(cuò)誤的組中,那么可以在Project Navigator中把它重新拖動(dòng)到正確的組中。Resources組并沒(méi)有什么特別的地方,它就是按照定義應(yīng)該保存不是源代碼的文件的地方。
玩家圖像alien.png和alien-hd.png就在隨書(shū)附帶的DoodleDrop項(xiàng)目的Resources文件夾中。也可以挑選你自己的圖像,只要圖像的尺寸是64×64像素或128×128像素(HD格式,帶有-hd后綴)即可。cocos2d自動(dòng)為帶有Retina顯示屏的iPhone和iPod Touch設(shè)備使用HD文件,標(biāo)準(zhǔn)分辨率(SD)的文件只用于iPhone 3GS。cocos2d還能識(shí)別另外兩種文件后綴:-ipad用于iPad和iPad 2,-ipadhd用于帶有Retina顯示屏的第3代(或更新的)iPad。
對(duì)于專用于Retina設(shè)備和iPad的資源,cocos2d默認(rèn)使用帶有-hd、-ipad和ipadhd后綴的文件。普通的iOS應(yīng)用程序不會(huì)使用它們。這些應(yīng)用程序要想使用高分辨率圖像,必須使用蘋果公司的@2x文件擴(kuò)展名。雖然在cocos2d應(yīng)用程序中也可以使用@2x擴(kuò)展名,但是cocos2d的文檔警告用戶不要使用該擴(kuò)展名。
提示:
一個(gè)常被問(wèn)起的問(wèn)題是在非Retina設(shè)備上簡(jiǎn)單地縮小HD圖像是否合適。這么做是不合適的,原因有兩個(gè)。一個(gè)原因是內(nèi)存限制。即使最早的Retina設(shè)備的內(nèi)存也是非Retina設(shè)備的2倍以上。讓非Retina設(shè)備加載HD圖像,占用的內(nèi)存將是已被縮小并打包的SD圖像的4倍。另一個(gè)原因是,加載Retina圖像的時(shí)間要比加載SD圖像長(zhǎng)得多,在較老、較慢的設(shè)備(比如沒(méi)有Retina顯示屏的那些設(shè)備)上這個(gè)問(wèn)題更加明顯。
反過(guò)來(lái),在應(yīng)用程序中只使用標(biāo)準(zhǔn)分辨率的資源也不是十分合適。這樣將無(wú)法利用Retina顯示屏的高分辨率,而且應(yīng)用程序的圖像質(zhì)量在Retina設(shè)備上不如高分辨率的圖像。放大和圖像處理算法再好,也不能讓標(biāo)準(zhǔn)分辨率圖像在Retina設(shè)備上呈現(xiàn)清晰而鮮明的效果。正因如此,應(yīng)該將游戲的所有資源設(shè)計(jì)為使用高分辨率,然后在需要的時(shí)候再縮小其大小。唯一需要注意的是,尺寸大小應(yīng)該能夠被2整除。
Xcode隨后會(huì)詢問(wèn)添加文件的方式和位置細(xì)節(jié),如圖4-6所示。確保在Add To Targets區(qū)域選中了所有會(huì)使用該文件的目標(biāo)程序,當(dāng)然在這里只有DoodleDrop,不過(guò)在Kobold2D項(xiàng)目中最好也把該文件添加到Mac OS X目標(biāo)中。如果文件還沒(méi)有存儲(chǔ)到項(xiàng)目的文件夾中,就應(yīng)該選中“Copy items into destination group’s folder(if needed)”復(fù)選框。如果無(wú)法確定,就選中該復(fù)選框,最壞的情況也就只是有相同文件的副本。如果不選中該復(fù)選框,那么最壞的情況會(huì)是把項(xiàng)目添加到源代碼管理程序或者在壓縮并分享項(xiàng)目時(shí)發(fā)生文件丟失錯(cuò)誤。
提示:
iOS游戲首選的圖像文件格式是PNG(Portable Network Graphic,便攜式網(wǎng)絡(luò)圖像)。這是一種壓縮文件格式,然而與JPEG文件不同的是,PNG采用了無(wú)損壓縮,保留了原始圖像的所有像素。你也可以保存不經(jīng)壓縮的JPEG文件,不過(guò)對(duì)于同一圖像,PNG文件的大小明顯要比未經(jīng)壓縮的JPEG文件小。但是這只會(huì)影響應(yīng)用的大小,而不會(huì)影響紋理對(duì)內(nèi)存(RAM)的使用。不使用JPEG文件的另一個(gè)原因是,cocos2d在iOS上加載這些文件的速度很慢。我上次測(cè)量的結(jié)果是它們比PNG文件慢8倍。第6章將介紹TexturePacker,一個(gè)用于管理圖像的工具。它允許將圖像轉(zhuǎn)換為各種壓縮格式或減少圖像的色深,同時(shí)通過(guò)抖色和其他技術(shù)保留盡可能高的圖像質(zhì)量。

?

圖4-6? 每次添加資源文件時(shí)都會(huì)出現(xiàn)這個(gè)對(duì)話框,大多數(shù)情況下你都應(yīng)該使用默認(rèn)設(shè)置
現(xiàn)在我們要向游戲場(chǎng)景中加入玩家精靈了。我會(huì)將它們作為CCSprite*類型的成員變量加入到GameLayer類中。就目前來(lái)看,這樣做比較容易,而且游戲也足夠簡(jiǎn)單,可以將所有組件都加入到相同的類中。通常不推薦這種方法,在之后的項(xiàng)目中我們將會(huì)創(chuàng)建單獨(dú)的類來(lái)保存每個(gè)游戲組件,以符合好的代碼設(shè)計(jì)要求。
程序清單4-4展示了如何在GameLayer的頭文件中添加CCSprite*類型的成員變量。
程序清單4-4? 將CCSprite*類型的成員變量添加到GameLayer類中
#import < Foundation/Foundation.h>
#import "cocos2d.h"

@interface GameLayer : CCLayer
{
CCSprite* player;
}
+(id) scene;

@end
程序清單4-5是加入到init方法中的代碼,它的功能是初始化精靈,將精靈賦給成員變量,并設(shè)置精靈到屏幕底部中間的位置,同時(shí)還啟用了加速計(jì)輸入功能。
程序清單4-5? 啟用加速計(jì)輸入,創(chuàng)建并定位玩家精靈
-(id) init
{
if ((self = [super init]))
{
CCLOG(@"%@: %@", NSStringFromSelector(_cmd), self);

self.isAccelerometerEnabled = YES;

player = [CCSprite spriteWithFile:@"alien.png"];
[self addChild:player z:0 tag:1];

CGSize screenSize = [CCDirector sharedDirector].winSize;
float imageHeight = player.texture.contentSize.height;
player.position = CGPointMake(screenSize.width / 2, imageHeight / 2);
}

return self;
}
玩家精靈已添加為層的子節(jié)點(diǎn),它的標(biāo)記值是1,隨后我們將使用這個(gè)標(biāo)記值識(shí)別它并把它與其他的精靈區(qū)分開(kāi)。這里的文件名使用了標(biāo)準(zhǔn)分辨率圖像的文件名,即alien.png。在Retina設(shè)備上,cocos2d會(huì)自動(dòng)加載alien-hd.png。如果沒(méi)有對(duì)應(yīng)的-hd文件,cocos2d會(huì)加載標(biāo)準(zhǔn)分辨率圖像。此時(shí),圖像在Retina設(shè)備上看起來(lái)比在非Retina設(shè)備上小。提供所有圖像資源的-hd版本是一種很好的做法。
警告:
記住,在iOS設(shè)備上文件名是區(qū)分大小寫的。如果你試圖加載Alien.png或ALIEN.PNG,在模擬器上會(huì)成功加載文件而在iOS設(shè)備上則不會(huì),因?yàn)槲募嬲拿质莂lien.png,字母全部小寫。這就是堅(jiān)持統(tǒng)一的命名約定——例如強(qiáng)制所有文件名使用小寫字母——的意義所在。為什么要用小寫?因?yàn)槿看髮懙奈募茈y辨認(rèn)。
我們將position屬性的x值設(shè)為屏幕寬度的一半,從而使玩家精靈的初始位置水平居中。在垂直位置上,我們想使玩家精靈紋理的底端與屏幕底端對(duì)齊。如果你記得前一章的內(nèi)容,就會(huì)知道精靈紋理是以其中心點(diǎn)作為位置坐標(biāo)值的。將精靈的垂直位置設(shè)為0會(huì)導(dǎo)致紋理的下半邊陷入屏幕底端。這不是我們想要的,我們要把它往上挪半個(gè)紋理的高度。
可以通過(guò)調(diào)用player.texture.contentSize.height返回精靈紋理的內(nèi)容尺寸。什么是內(nèi)容尺寸?在第3章中,我講到iOS中紋理尺寸的大小只能是2的方冪。但是實(shí)際的圖像尺寸可能會(huì)比紋理尺寸小。例如,如果原始圖像的尺寸為100×100像素,那么紋理尺寸就是128×128像素。紋理的contentSize屬性會(huì)返回原始圖像的尺寸,也就是100×100像素。大部分情況下,處理的都是內(nèi)容尺寸而不是紋理尺寸。即使圖像是2的冪,也應(yīng)該使用contentSize,因?yàn)榧y理可能是包含多個(gè)圖像的紋理圖冊(cè)。第6章將詳細(xì)討論紋理圖冊(cè)。
將圖像高度的一半設(shè)為position屬性的y值后,精靈圖像恰好能與屏幕底端對(duì)齊。
提示:
無(wú)論何時(shí)都要盡量避免使用固定的位置值。如果你只是把玩家位置設(shè)為(160,32),此時(shí)你就做了兩個(gè)本該避免的假定。第一,你假定了屏幕寬度為320像素,但并不是每個(gè)iOS設(shè)備都是這樣的。第二,你假定了圖像高度是64像素,然而那也是可能會(huì)改變的。一旦你開(kāi)始像這樣做假定,代碼就會(huì)喪失一部分靈活性,修改起來(lái)需要很多時(shí)間。
我用了很多代碼來(lái)定位對(duì)象,但長(zhǎng)遠(yuǎn)來(lái)看這樣做會(huì)節(jié)約大量時(shí)間。可以將項(xiàng)目部署到不同的設(shè)備,也可以使用不同尺寸的圖像,無(wú)論如何項(xiàng)目都會(huì)正常運(yùn)行。你不再需要修改這段特別的代碼了。程序員面對(duì)的最費(fèi)時(shí)的壞事情之一就是——不得不修改那些依據(jù)假定而編寫的代碼。
4.5? 加速計(jì)輸入
最后一步,我們來(lái)使玩家精靈能夠左右傾斜。在第3章中我已經(jīng)說(shuō)明,應(yīng)該為接收加速計(jì)輸入的層添加accelerometer方法。這里我使用了acceleration.x參數(shù),將它乘以10后加到玩家精靈的位置值上,從而加速玩家精靈的運(yùn)動(dòng):
-(void) accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
CGPoint pos = player.position;
pos.x + = acceleration.x * 10;
player.position = pos;
}
注意到奇怪之處了嗎?上面的三行代碼用一行就能寫好:
// ERROR: lvalue required as left operand of assignment
player.position.x += acceleration.x * 10;
然而,與其他編程語(yǔ)言(如Java、C++或C#等)不同,像player.position.x += value這樣的語(yǔ)句對(duì)Objective-C中的屬性是不起作用的。position屬性的類型是CGPoint,這是一個(gè)普通的C結(jié)構(gòu)體。Objective-C中的屬性不能直接向結(jié)構(gòu)體的域賦值。問(wèn)題出在Objective-C中屬性的工作方式,以及Objective-C所基于的C語(yǔ)言的賦值機(jī)制。
player.position.x語(yǔ)句實(shí)際上是調(diào)用了position的getter方法[player position],這意味著你實(shí)際上獲得了一個(gè)臨時(shí)的position值,并試圖改變這個(gè)臨時(shí)的CGPoint對(duì)象的x成員變量。但之后這個(gè)臨時(shí)的CGPoint對(duì)象會(huì)被丟棄。于是position的setter方法[player setPosition]就不會(huì)自動(dòng)被調(diào)用了。所以只能直接對(duì)player.position進(jìn)行賦值,在本例中就是賦給它一個(gè)新的CGPoint對(duì)象。在使用Objective-C時(shí),你必須接受這個(gè)令人遺憾的問(wèn)題,而且如果你有過(guò)編寫Java、C++或C#的經(jīng)歷,沒(méi)準(zhǔn)還得改變你的編程習(xí)慣。
因此,前面的代碼必須創(chuàng)建一個(gè)臨時(shí)的CGPoint對(duì)象,修改position的x域,然后把臨時(shí)的CGPoint賦給player.position。在Objective-C中是必須這么做的。
4.6? 首次測(cè)試運(yùn)行
現(xiàn)在你的項(xiàng)目應(yīng)與本章附帶的DoodleDrop02項(xiàng)目不相上下。馬上測(cè)試一下吧。你要確認(rèn)已選擇在設(shè)備上運(yùn)行應(yīng)用,因?yàn)槟M器不會(huì)獲得加速計(jì)輸入。檢驗(yàn)當(dāng)前版本中的加速計(jì)輸入表現(xiàn)如何。
如果尚未在Xcode中為這個(gè)項(xiàng)目安裝你的開(kāi)發(fā)授權(quán),將會(huì)產(chǎn)生“code sign”錯(cuò)誤。在iOS設(shè)備上運(yùn)行程序時(shí)需要代碼簽名證書(shū)。請(qǐng)查閱蘋果公司的文檔以了解如何創(chuàng)建和安裝必要的開(kāi)發(fā)授權(quán)文檔(http://developer.apple.com/ios/manage/provisioningpro?les/howto.action)。
4.7? 玩家速度
注意到加速計(jì)輸入哪里不正常了嗎?是的,它反應(yīng)遲緩,移動(dòng)不暢。這是因?yàn)橥婕揖`并沒(méi)執(zhí)行真實(shí)的加速和減速。讓我們修改一下它。修改后的代碼在DoodleDrop03項(xiàng)目里。
實(shí)現(xiàn)加速與減速的概念不在于直接改變玩家的位置值,而是使用單獨(dú)的CGPoint變量作為速度矢量。每次接收到加速計(jì)事件時(shí),速度矢量就加上從加速計(jì)得到的輸入。當(dāng)然,這意味著我們必須把速度限制在一個(gè)任意的最大值內(nèi),否則減速時(shí)就要花點(diǎn)時(shí)間了。不管有沒(méi)有接收到加速計(jì)輸入,在每一幀都把速度加到玩家位置上。
注意:
為什么不使用動(dòng)作來(lái)移動(dòng)玩家精靈呢?無(wú)論何時(shí)你需要頻繁地——如每秒數(shù)次——改變對(duì)象的速度或方向,使用move動(dòng)作都不是一個(gè)好的選擇。動(dòng)作適用于相對(duì)使用期較長(zhǎng)的一次性對(duì)象,所以頻繁創(chuàng)建新對(duì)象在分配和釋放內(nèi)存上增加了額外開(kāi)銷,這會(huì)使游戲性能大幅下降。
更糟糕的是,如果不為動(dòng)作留出一點(diǎn)時(shí)間,動(dòng)作是不會(huì)執(zhí)行的。這就是在每幀添加新動(dòng)作來(lái)替換前一個(gè)卻沒(méi)有任何效果的原因。很多cocos2d開(kāi)發(fā)者都曾偶然發(fā)現(xiàn)過(guò)這個(gè)看似古怪的現(xiàn)象。
例如,如果在每幀都停止所有動(dòng)作并為對(duì)象添加一個(gè)新的名為MoveBy的動(dòng)作,對(duì)象不會(huì)有一丁點(diǎn)的移動(dòng)!MoveBy動(dòng)作只會(huì)在下一幀改變對(duì)象的位置。但是在下一幀你已經(jīng)停止所有動(dòng)作,并加入另一個(gè)新的MoveBy動(dòng)作了。這樣做下去只會(huì)讓對(duì)象寸步不移。這就像老生常談的關(guān)于驢的那一套故事:推得越使勁它就越犟,并且在原地不動(dòng)彈。
讓我們來(lái)看一下對(duì)代碼所做的修改。在頭文件中加入了playerVelocity變量:
@interface GameLayer : CCLayer
{
CCSprite* player;

CGPoint playerVelocity;
}
你可能想知道為何使用CGPoint替代float。這是考慮到你以后可能會(huì)加速或減速一點(diǎn)點(diǎn)。為今后的擴(kuò)展做些準(zhǔn)備總沒(méi)有壞處。
程序清單4-6是加速計(jì)的代碼,其中使用速度代替了對(duì)玩家位置的直接修改。這段代碼采用了三個(gè)設(shè)計(jì)參數(shù):減速值、加速計(jì)靈敏度和最大速度。這些參數(shù)沒(méi)有最優(yōu)值;你需要調(diào)整數(shù)值,找到最適合你游戲的設(shè)置,因此得名“設(shè)計(jì)參數(shù)”。
減速是指減少當(dāng)前速度,之后速度將加上新的加速計(jì)值與靈敏度相乘后的數(shù)值。減速值越低,玩家精靈操作外星人改變方向的速度就越快。靈敏度越高,玩家精靈對(duì)加速計(jì)輸入的反應(yīng)就越敏感。由于這些數(shù)值是對(duì)同一數(shù)值進(jìn)行修改,它們相互作用且相互影響,因此一定記得每次只調(diào)整一個(gè)值。
程序清單4-6? 通過(guò)GameLayer實(shí)現(xiàn)得到playerVelocity
-(void) accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
// controls how quickly velocity decelerates (lower = quicker to change direction)
float deceleration = 0.4f;
// determines how sensitive the accelerometer reacts (higher = more sensitive)
float sensitivity = 6.0f;
// how fast the velocity can be at most
float maxVelocity = 100;

// adjust velocity based on current accelerometer acceleration
playerVelocity.x = playerVelocity.x * deceleration + acceleration.x * ?
sensitivity;

// we must limit the maximum velocity of the player sprite, in both directions
if (playerVelocity.x > maxVelocity)
{
playerVelocity.x = maxVelocity;
}
else if (playerVelocity.x - maxVelocity)
{
playerVelocity.x = - maxVelocity;
}
}
現(xiàn)在playerVelocity能夠改變了,但如何把速度加到玩家精靈的位置值上呢?可以在GameLayer的init方法中指定如下update方法:
// schedules the –(void) update:(ccTime)delta method to be called every frame
[self scheduleUpdate];
同樣需要添加–(void) update:(ccTime)delta方法,如程序清單4-7所示。已指定的update方法在每一幀都會(huì)被調(diào)用,而那就是我們要在玩家精靈的位置值上加上速度的地方。這樣我們就可以做到:無(wú)論加速計(jì)頻率的大小如何,都能產(chǎn)生流暢平滑的運(yùn)動(dòng)。
程序清單4-7? 用當(dāng)前速度更新玩家精靈的位置
-(void) update:(ccTime)delta
{
// Keep adding up the playerVelocity to the player's position
CGPoint pos = player.position;
pos.x + = playerVelocity.x;

// The Player should also be stopped from going outside the screen
CGSize screenSize = [CCDirector sharedDirector].winSize;
float imageWidthHalved = player.texture.contentSize.width * 0.5f;
float leftBorderLimit = imageWidthHalved;
float rightBorderLimit = screenSize.width - imageWidthHalved;

// preventing the player sprite from moving outside the screen
if (pos.x < leftBorderLimit)
{
pos.x = leftBorderLimit;
playerVelocity = CGPointZero;
}

else if (pos.x > rightBorderLimit)
{
pos.x = rightBorderLimit;
playerVelocity = CGPointZero;
}

// assigning the modified position back
player.position = pos;
}
邊界檢查可以防止玩家精靈顯示在屏幕外。我們不得不再一次把玩家精靈紋理的contentSize(內(nèi)容尺寸)考慮在內(nèi),因?yàn)橥婕揖`是以精靈圖像的中心為準(zhǔn)計(jì)算位置的,我們不希望圖像的任何一邊離開(kāi)屏幕。為此,計(jì)算imageWidthHalved的值,然后用它檢查剛剛更新的玩家位置是否在左右邊界之內(nèi)。上面的代碼稍顯冗長(zhǎng),但這樣更容易理解。現(xiàn)在構(gòu)建并運(yùn)行項(xiàng)目,體驗(yàn)?zāi)軌蚩刂仆婕揖`的感覺(jué)。
提示:
如果玩過(guò)Tilt to Live之類的游戲,可能會(huì)注意到這里實(shí)現(xiàn)的簡(jiǎn)單的加速計(jì)控制無(wú)法像在那些游戲中一樣有一種動(dòng)態(tài)的感覺(jué)。這是因?yàn)橐獙?shí)現(xiàn)流暢、動(dòng)態(tài)的加速計(jì)控制,需要對(duì)加速計(jì)輸入進(jìn)行過(guò)濾。在Kobolds2D中,使用KKInput類的屬性可以獲得高通(瞬時(shí))和低通(平滑)過(guò)濾后的加速計(jì)輸入:
float smoothed = [KKInput sharedInput].acceleration.smoothedX;
利用加速計(jì)控制的游戲通常會(huì)使用低通過(guò)濾器。“低通”意味著過(guò)濾掉加速度突然且極端的變化,從而使得到的結(jié)果值十分平滑。下面是由加速計(jì)輸入值(rawX/rawY)和常量filterFactor(范圍為0.0~1.0)得到新的smoothedX/smoothedY值(示例變量)的低通過(guò)濾器。0.1是一個(gè)不錯(cuò)的過(guò)濾因子,表示在新的平滑后的值中只考慮了10%的當(dāng)前原始加速計(jì)值:
smoothedX = (rawX * filterFactor)? +? (smoothedX * (1.0 - filterFactor));
smoothedY? =? (rawY * filterFactor)? +? (smoothedY * (1.0 - filterFactor));
4.8? 添加障礙物
在往游戲中加入一些讓玩家躲避的東西之前,這個(gè)游戲還沒(méi)有什么可玩性。接下來(lái)在項(xiàng)目里加入一些令人憎惡的東西:六足人造蜘蛛。有誰(shuí)不想躲著它們嗎?
與玩家精靈一樣,需要把spider.png和spider-hd.png加入到Resources組中。然后在GameLayer.h的接口中加入3個(gè)新的成員變量:在程序清單4-9中出現(xiàn)的NSMutableArray類引用spiders,以及在程序清單4-12中使用的spiderMoveDuration和numSpidersMoved:
@interface GameLayer : CCLayer
{
CCSprite* player;
CGPoint playerVelocity;

NSMutableArray* spiders;
float spiderMoveDuration;
int numSpidersMoved;
}
警告:
在代碼中應(yīng)該避免使用CCArray。CCArray是NSArray和NSMutableArray的快速替代版本,但是它只是快了一點(diǎn)點(diǎn),使用它幾乎不會(huì)對(duì)幀率產(chǎn)生影響。而它的一些方法(如insertAtIndex或removeObjects)要比NSMutableArray的相同方法慢得多。CCArray最大的問(wèn)題是過(guò)去已經(jīng)存在一些嚴(yán)重的bug,比如與ARC的兼容性問(wèn)題。它也不能支持NSArray/NSMutableArray的全部功能。例如,無(wú)法枚舉帶有block對(duì)象的CCArray,這使得它不適合用于并行處理(例如,通過(guò)Grand Central Dispatch來(lái)完成)。整體上看,NSMutableArray要比CCArray更可靠,兼容性更好,并且缺陷更少。任何時(shí)候,我都愿意用降低一些性能來(lái)?yè)Q取高可靠性。cocos2d在內(nèi)部使用了CCArray,不過(guò)就內(nèi)部使用來(lái)看,CCArray經(jīng)過(guò)了大量測(cè)試,證明沒(méi)有問(wèn)題,所以這種用法我是可以接受的。
與此同時(shí),在GameLayer的init方法中,在scheduleUpdate后面加上對(duì)initSpiders方法的調(diào)用,我們將在后面討論它:
-(id) init
{
if ((self = [super init]))
{
...
[self scheduleUpdate];
[self initSpiders];
}
return self;
}
隨后,我們會(huì)向GameLayer類中加入一大段代碼,先從initSpiders方法開(kāi)始,它創(chuàng)建了蜘蛛精靈,如程序清單4-8所示。
程序清單4-8? 為了更好地進(jìn)入,蜘蛛精靈被初始化并添加到NSMutableArray中
-(void) initSpiders
{
CGSize screenSize = [CCDirector sharedDirector].winSize;

// using a temporary spider sprite is the easiest way to get the image's size
CCSprite* tempSpider = [CCSprite spriteWithFile:@"spider.png"];

float imageWidth = tempSpider.texture.contentSize.width;

// Use as many spiders as can fit next to each other over the whole screen width.
int numSpiders = screenSize.width / imageWidth;

// Initialize the spiders array using alloc.
spiders = [NSMutableArray arrayWithCapacity:numSpiders];
for (int i = 0; i < numSpiders; i++)
{
CCSprite* spider = [CCSprite spriteWithFile:@"spider.png"];
[self addChild:spider z:0 tag:2];

// Also add the spider to the spiders array.
[spiders addObject:spider];
}

// call the method to reposition all spiders
[self resetSpiders];
}
這里要說(shuō)明的是,創(chuàng)建名為tempSpider的CCSprite對(duì)象只是為了獲得精靈圖像的寬度,然后用它決定蜘蛛精靈的數(shù)量。獲得圖像尺寸最簡(jiǎn)單的方法就是創(chuàng)建臨時(shí)的CCSprite對(duì)象。注意,我沒(méi)有把這個(gè)tempSpider對(duì)象作為子節(jié)點(diǎn)加到任何其他節(jié)點(diǎn)上,也沒(méi)有把它分配給實(shí)例變量。這意味著當(dāng)程序執(zhí)行離開(kāi)initSpiders方法后,ARC會(huì)知道tempSpider對(duì)象已經(jīng)不再使用,并自動(dòng)釋放其內(nèi)存。
與其形成對(duì)比的是名為spiders的數(shù)組,我用它來(lái)保存對(duì)所有蜘蛛精靈的引用。這個(gè)數(shù)組被分配給實(shí)例變量spiders,因此,在GameLayer對(duì)象本身被釋放以前,ARC不會(huì)釋放該數(shù)組對(duì)象。使用ARC時(shí),不需要自己以任何方式釋放spiders數(shù)組。
在程序清單4-8的結(jié)尾,調(diào)用了[self resetSpiders]方法,這個(gè)方法的代碼在程序清單4-9中。將精靈的初始化和定位分開(kāi)處理的原因在于:游戲總會(huì)結(jié)束,之后游戲?qū)⒈恢刂谩W顬楦咝У淖龇ň褪菍⑺杏螒驅(qū)ο笠苿?dòng)到它們的初始位置。然而,一旦游戲趨于復(fù)雜,這種做法將不具備可行性。最終,最簡(jiǎn)單的做法就是以玩家的等待為代價(jià)重新加載全部場(chǎng)景。
警告:
在重新加載場(chǎng)景時(shí),你可能想使用[[CCDirector sharedDirector] replaceScene: self];來(lái)重新加載同一場(chǎng)景。但是這會(huì)導(dǎo)致程序崩潰,因?yàn)閟elf是當(dāng)前正在運(yùn)行的場(chǎng)景。在cocos2d中嘗試用正在運(yùn)行的場(chǎng)景替換其自身會(huì)導(dǎo)致程序崩潰。實(shí)際上,必須創(chuàng)建GameLayer類的新實(shí)例:[[CCDirector sharedDirector] replaceScene:[GameLayer scene]];。
程序清單4-9? 重設(shè)蜘蛛精靈的位置
-(void) resetSpiders
{
CGSize screenSize = [CCDirector sharedDirector].winSize;

// Get any spider to get its image width
CCSprite* tempSpider = [spiders lastObject];
CGSize size = tempSpider.texture.contentSize;
int numSpiders = [spiders count];
for (int i = 0; i < numSpiders; i++)
{
// Put each spider at its designated position outside the screen
CCSprite* spider = [spiders objectAtIndex:i];
spider.position = CGPointMake(size.width * i + size.width * 0.5f,?
screenSize.height + size.height);

[spider stopAllActions];
}

// Schedule the spider update logic to run at the given interval.
[self schedule:@selector(spidersUpdate:) interval:0.7f];

// reset the moved spiders counter and spider move duration (affects speed)
numSpidersMoved = 0;
spiderMoveDuration = 4.0f;
}
我再一次臨時(shí)獲取了某個(gè)已有的蜘蛛精靈,然后通過(guò)紋理的contentsize屬性獲得它的圖像尺寸。這里我沒(méi)有創(chuàng)建新的精靈,因?yàn)橐延型惥`存在了,并且由于所有的蜘蛛都使用相同尺寸大小的同一圖像,我甚至不用關(guān)心獲取的是哪個(gè)蜘蛛,因此我只是簡(jiǎn)單地獲取了數(shù)組的最后一項(xiàng)。
接下來(lái)修改每個(gè)蜘蛛的位置,使它們整體橫跨整個(gè)屏幕的寬度。還是同樣的原因,蜘蛛精靈的紋理以其中心點(diǎn)作為位置,position屬性的x值加上了圖像寬度的一半。至于高度,每個(gè)蜘蛛也被設(shè)為高于屏幕頂端一個(gè)圖像高度。這個(gè)數(shù)值是任意的,這里我要使圖像不可見(jiàn),能達(dá)此目的者均可。由于重置后蜘蛛仍然可能在移動(dòng),因此要停止它的全部動(dòng)作。
提示:
如果不是絕對(duì)必要的話,為了節(jié)約CPU資源,最好不要在for或其他循環(huán)語(yǔ)句中使用方法調(diào)用作為循環(huán)條件。本例中創(chuàng)建numSpiders變量來(lái)保存[spiders count]的調(diào)用結(jié)果,然后將其用作for循環(huán)的循環(huán)條件。由于在循環(huán)過(guò)程中數(shù)組本身并未被修改,因此數(shù)組的計(jì)數(shù)值保持不變。這就是為何我能保存這個(gè)值并在for循環(huán)中省去對(duì)[spiders count]的重復(fù)調(diào)用。
我還指定spidersUpdate:選擇器每0.7秒運(yùn)行一次——這是另一個(gè)蜘蛛從屏幕頂端落下的時(shí)間間隔。如果選擇器已被指定,cocos2d會(huì)用一條日志消息指出這一點(diǎn),你可以忽略這條消息。cocos2d并不會(huì)再次指定選擇器,而是會(huì)更新已指定選擇器的時(shí)間間隔。如程序清單4-10所示,spidersUpdate:方法會(huì)隨機(jī)挑選一個(gè)已經(jīng)存在的蜘蛛,檢查它是否空閑,然后使用一系列動(dòng)作操作它從屏幕上方落下。
程序清單4-10? spridersUpdate:——讓蜘蛛頻繁下落的方法
-(void) spidersUpdate:(ccTime)delta
{
// Try to find a spider which isn't currently moving.
for (int i = 0; i < 10; i++)
{
int randomSpiderIndex = CCRANDOM_0_1() * spiders.count;
CCSprite* spider = [spiders objectAtIndex:randomSpiderIndex];

// If the spider isn't moving it won't have any running actions.
if (spider.numberOfRunningActions == 0)
{
// This is the sequence which controls the spiders' movement
[self runSpiderMoveSequence:spider];

// Only one spider should start moving at a time.
break;
}
}
}
我還從未對(duì)任何程序清單置之不理,是吧?你也許想知道為什么這里我要循環(huán)迭代10次來(lái)得到一個(gè)隨機(jī)的蜘蛛。原因在于,我不知道隨機(jī)生成的索引值對(duì)應(yīng)的蜘蛛是不是活動(dòng)的,所以要確認(rèn)最終隨機(jī)選出的蜘蛛當(dāng)前是空閑的。如果10次之后——當(dāng)然,這個(gè)數(shù)字是任意的——仍然沒(méi)有隨機(jī)選出一個(gè)空閑的蜘蛛,就會(huì)跳過(guò)這次更新,然后等待下一次。
也可以使用do/while循環(huán)進(jìn)行不斷嘗試,直到找到空閑的蜘蛛為止。但有一種可能,即此刻所有的蜘蛛都在移動(dòng)——這取決于設(shè)計(jì)參數(shù),如新蜘蛛落下的頻率。游戲會(huì)為嘗試尋找空閑的蜘蛛而無(wú)限循環(huán),從而鎖死。此外,我并不喜歡太賣力;對(duì)于這個(gè)游戲而言,另一個(gè)蜘蛛等個(gè)幾秒再落下也并無(wú)大礙。雖然如此,如果查看DoodleDrop03項(xiàng)目,就會(huì)發(fā)現(xiàn)我加入了日志記錄語(yǔ)句,輸出找到空閑蜘蛛的重試次數(shù)。
由于蜘蛛執(zhí)行的唯一動(dòng)作就是一系列的運(yùn)動(dòng),我只要檢查蜘蛛當(dāng)前是否執(zhí)行任何動(dòng)作即可。如果沒(méi)有執(zhí)行動(dòng)作,我就假定它是空閑的。然后執(zhí)行程序清單4-11所示的runSpiderMoveSequence方法。
程序清單4-11? 通過(guò)動(dòng)作序列控制蜘蛛的運(yùn)動(dòng)
-(void) runSpiderMoveSequence:(CCSprite*)spider
{
// Slowly increase the spider speed over time.
numSpidersMoved++;
if (numSpidersMoved % 8 == 0 && spiderMoveDuration > 2.0f)
{
spiderMoveDuration - = 0.1f;
}

// This is the sequence which controls the spiders' movement.
CGPoint belowScreenPosition = CGPointMake(spider.position.x,?
-spider.texture.contentSize.height);
CCMoveTo* move = [CCMoveTo actionWithDuration:spiderMoveDuration
position:belowScreenPosition];

CCCallBlock* callDidDrop = [CCCallBlock actionWithBlock:^void(){
// move the droppedSpider back up outside the top of the screen
CGPoint pos = spider.position;
CGSize screenSize = [CCDirector sharedDirector].winSize;
pos.y = screenSize.height + spider.texture.contentSize.height;
spider.position = pos;
}];

CCSequence* sequence = [CCSequence actions:move, callDidDrop, nil];
[spider runAction:sequence];
}
runSpiderMoveSequence方法記錄下了落下的蜘蛛數(shù)目。每落下8個(gè)蜘蛛,spiderMove- Duration就降低,從而增加所有蜘蛛的速度。你也許不熟悉%,它被稱為求模操作符。求模運(yùn)算的結(jié)果是左操作數(shù)除以右操作數(shù)的余數(shù),即如果numSpidersMoved可以被8整除,那么求模結(jié)果為0。
動(dòng)作序列只包含了一個(gè)CCMoveTo動(dòng)作和一個(gè)CCCallBlock動(dòng)作。動(dòng)作還有改進(jìn)的空間,可以使它就像真正的六足蜘蛛人那樣,下落一點(diǎn)點(diǎn),停住,然后一路下到底。這個(gè)任務(wù)就留給你了,不過(guò)在最終版的DoodleDrop項(xiàng)目中,你可以找到一個(gè)示例實(shí)現(xiàn)。
到目前為止,唯一重要的是知道我選擇了在傳遞給CCCallBlock動(dòng)作的block函數(shù)中重置了蜘蛛的位置。這個(gè)block函數(shù)可以簡(jiǎn)單地使用與runSpiderMoveSequence方法相同的spider變量。它在蜘蛛的運(yùn)動(dòng)完成后調(diào)用,即蜘蛛已經(jīng)掉到了玩家角色的下面。通過(guò)使用這個(gè)block函數(shù),你就不需要花大力氣找出正確的蜘蛛。而后,將蜘蛛的位置重置為屏幕上方。程序清單4-12將程序清單4-11中的block函數(shù)單獨(dú)列了出來(lái)。
程序清單4-12? 在CCCallBlock中重設(shè)蜘蛛位置,使之可以從屏幕上方重新落下
CCCallBlock* callDidDrop = [CCCallBlock actionWithBlock:^void(){
// move the droppedSpider back up outside the top of the screen
CGPoint pos = spider.position;
CGSize screenSize = [CCDirector sharedDirector].winSize;
pos.y = screenSize.height + spider.texture.contentSize.height;
spider.position = pos;
}];
到目前為止,一切順利。我猜你一定迫不及待地想試玩一下。我想你會(huì)很快注意到游戲還是缺了些東西。小提示:看一下下面的標(biāo)題。

?

《ios cocos2d 2 游戲開(kāi)發(fā)實(shí)戰(zhàn)(第三版)》試讀電子書(shū)免費(fèi)提供,有需要的留下郵箱。

?

?

?

轉(zhuǎn)載于:https://my.oschina.net/louiseliu/blog/304218

總結(jié)

以上是生活随笔為你收集整理的iOS cocos2d 2游戏开发实战(第3版)---你的第一个游戏!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。