如何用cocos2d-x来开发简单的Uphone游戏:(二) 移动的精灵
三、添加一個精靈
?
我們先用個簡單的方式,把player, projectile, target三個PNG文件拷貝到 D:\Work7\NEWPLUS\TDA_DATA\UserData 目錄下,這使其可以在模擬器上直接通過文件路徑訪問到。Uphone有其資源打包的方式,圖片和音樂都可以打包到動態庫文件內,這個另外會有教程描述,我們這里先讓事情簡單化。
?? ? ? ??? ? ? ??
關于cocos2d坐標系統的規則,簡而言之就是左下角為原點,向上向右按像素遞增,這在Wenderlich的原文中有詳細描述,我們這里就不再贅述了。直接切入代碼
現在我們在HelloWorldScene.cpp里面,找到bool HelloWorld::init()函數,把它替換成下面代碼
?
bool?HelloWorld::init(){
??//
??//?1.?super?init?first
??if?(?!CCLayer::init()?)
??{
????return?false;
??}
??/////
??//?2.?add?a?menu?item?with?"X"?image,?which?is?clicked?to?quit?the?program
??//????you?may?modify?it.
??//?add?a?"close"?icon?to?exit?the?progress.?it's?an?autorelease?object
??CCMenuItemImage?*pCloseItem?=?CCMenuItemImage::itemFromNormalImage(?"CloseNormal.png",?
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"CloseSelected.png",?
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?this,
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?menu_selector(HelloWorld::menuCloseCallback)?);
??pCloseItem->setPosition(?ccp(CCDirector::getSharedDirector()->getWinSize().width?-?20,?20)?);
??//?create?menu,?it's?an?autorelease?object
??CCMenu*?pMenu?=?CCMenu::menuWithItems(pCloseItem,?NULL);
??pMenu->setPosition(?CGPointZero?);
??this->addChild(pMenu);
??/////
??//?3.?add?your?codes?below...
??CGSize?winSize?=?CCDirector::getSharedDirector()->getWinSize();
??CCSprite?*player?=?CCSprite::spriteWithFile("Player.png",?
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CGRectMake(0,?0,?27,?40)?);
??player->setPosition(?ccp(player->getContentSize().width/2,?winSize.height/2)?);
??this->addChild(player);
??return?true;
}
?
其實我們只修改了 // 3. add your codes below 這段。
cocos2d-x和cocos2d-iphone的接口有細微的差別,不過你一旦習慣了這個差別,寫起代碼來就會很順手。
我們拋開添加"X"退出按鈕的一段,單純地看一下這兩段init函數的差異.
?
| //?cpp with cocos2d-x bool?HelloWorld::init() { ??if?(?CCLayer::init()?) ??{ ?? ?CGSize?winSize?=?CCDirector::getSharedDirector()->getWinSize(); ?? ?CCSprite?*player?=?CCSprite::spriteWithFile("Player.png",? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CGRectMake(0,?0,?27,?40)?); ?? ?player->setPosition(?ccp(player->getContentSize().width/2,? ??????????????????????????????winSize.height/2)?); ???this->addChild(player); ??} ??return?true; } | //?objc with cocos2d-iphone -(id)?init { ??if(?(self=[super?init]?))? ??{ ?? ?CGSize?winSize?=?[[CCDirector?sharedDirector]?winSize]; ?? ?CCSprite?*player?=?[CCSprite?spriteWithFile:@"Player.png" ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?rect:CGRectMake(0,?0,?27,?40) ]; ?? ?player.position?=?ccp(player.contentSize.width/2,? ?? ? ? ? ? ? ? ? ? ? ? ? ?winSize.height/2); ?? ?[self?addChild:player]; ??} ??return?self; }? |
?
轉換要點1. 雖然VC++中有super關鍵字,但linux下用gcc編譯時可不認。所以C++中不能用super::init(),而必須老老實實地指定是執行父類CCLayer::init()方法
?
2. 由于cpp里沒有property的概念,所以在objc里涉及property的地方,我們都用了get/set函數代替。于是,訪問CCDirector.SharedDirector屬性的代碼,就變成了CCDirector::getSharedDirector()函數調用,shared開頭小寫的s,也變成了大寫的S。同樣規則,取得winSize屬性的地方,則變成了getWinSize()函數調用。這段代碼中還有player->getContentSize()也受此影響。但訪問結構體中的變量,如winSize中的width和height則不需用getter封裝。
?
3. 設置類的屬性,如player.position = ,也改用setter實現,變成player->setPosition(...)
4. C++函數調用不需像OBJC那樣在每個參數前面說明這參數是干什么用的,比如rect:CGRectMake(...),只需直接輸入參數即可。另一方面, cocos2d-x仿照iOS實現了CGGeometry的一些函數,你可以在cocos2dx\include\CGGeometry.h里看到它們。除了CGRectMake,還有CGPointMake, CGSizeMake, CGPointZero, CGSizeZero, CGRectZero.
5. cocos2d-x所有的游戲元素, sprite,layer,scene,label,action等,都是new在heap上,并且用指針傳遞的,因此調用其成員函數一定是用->號,而不像objc里的點號
6. cpp里用this替代了objc的self關鍵字
?
7. cocos2d-x里的init函數改成返回bool類型了。由于cpp里沒有objc的"id"關鍵字,所以cocos2d-iphone里返回id的地方,都改成返回明確的類指針,或者bool型變量
好了,我們編譯運行一下,可以看到帶頭大哥一襲黑衣,很猥瑣地躲在黑色背景上,只露出一雙殺紅了的眼睛。為了游戲性,我們需要把背景顏色改成白的。只要簡單地修改,使HelloWorld不是繼承CCLayer,而是繼承CCColorLayer就行了。
在HelloWorldScene.h中,修改HelloWorld類聲明如下。
?(左邊為cpp代碼,也就是讀者現在應該使用的,右邊為Cocos2dSimpleGame原來使用cocos2d-iphone的objc代碼,用以對比參考。本系列后面的行文也都如此)
| //?cpp with cocos2d-x class?HelloWorld?:?public?cocos2d::CCColorLayer | //?objc with cocos2d-iphone @interface?HelloWorld?:?CCColorLayer |
?
然后在HelloWorld::init()函數實現中,修改剛開始的?
if?(?!CCLayer::init()?){
????return?false;
}
變成
if?(?!CCColorLayer::initWithColor(?ccc4(255,255,255,255)?)?){
????return?false;
}
這里小改了一下邏輯,原版objc里是如果super init成功,就BALA-BALA做后面的工作;我喜歡防御性編程,如果失敗則先做出錯處理、跳出,然后才繼續寫正確流程。這么做有兩個好處,一是不會寫到后面漏掉了錯誤處理,二是不用做太多層的if嵌套。這個是題外話了。拋開if的邏輯,我們來對比一下這句super init在cpp和objc的區別
| //?cpp?with?cocos2d-x if?(?CCColorLayer::initWithColor(?ccc4(255,255,255,255)?) | //?objc?with?cocos2d-iphone if?(?self?=?[super?initWithColor:ccc4(255,255,255,255)]?) |
?
轉換要點1. 首先,cpp的繼承默認為private繼承,所以類聲明的繼承處public關鍵字不可少?
2. cocos2d-iphone的作者Ricardo Quesada建議我們采用C++的命名空間把cocos2d整個庫包起來。而我們在這里既不想直接到頭文件里using namespace cocos2d;感染了所有包含這個頭文件的CPP文件,也不想把class HelloWorld歸到cocos2d命名空間內,所以HelloWorldScene.h頭文件里只好在每個cocos2d類前面加上命名空間cocos2d::
編譯后運行,你就可以看到帶頭大哥孤獨地站在白色背景上了,寂寞得淚流滿面
?
?
四、移動目標
有了帶頭大哥后,我們就需要添加一些蝦兵蟹將讓大哥砍。英雄人物一般不喜歡砍木樁,所以我們就用void addTarget()方法在屏幕右邊創建一些跑龍套的小兵,讓他們以隨機速度向左移動。
先到HelloWorldScene.h里添加函數聲明 void addTarget(); 然后回到HelloWorldScene.cpp里實現函數
| //?cpp?with?cocos2d-x void?HelloWorld::addTarget() { ??CCSprite?*target?=?CCSprite::spriteWithFile("Target.png",? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CGRectMake(0,0,27,40) ); ??//?Determine?where?to?spawn?the?target?along?the?Y?axis ??CGSize?winSize?=?CCDirector::getSharedDirector()->getWinSize(); ??int?minY?=?target->getContentSize().height/2; ??int?maxY?=?winSize.height?-??target->getContentSize().height/2; ??int?rangeY?=?maxY?-?minY; ??srand(?TimGetTicks()?); ??int?actualY?=?(?rand()?%?rangeY?)?+?minY; ??//?Create?the?target?slightly?off-screen?along?the?right?edge, ??//?and?along?a?random?position?along?the?Y?axis?as?calculated ??target->setPosition(? ?? ?ccp(winSize.width?+?(target->getContentSize().width/2),? ?? ?actualY)?); ??this->addChild(target); ??//?Determine?speed?of?the?target ??int?minDuration?=?(int)2.0; ??int?maxDuration?=?(int)4.0; ??int?rangeDuration?=?maxDuration?-?minDuration; ??srand(?TimGetTicks()?); ??int?actualDuration?=?(?rand()?%?rangeDuration?)?+?minDuration; ??//?Create?the?actions ??CCFiniteTimeAction*?actionMove?=? ?? ?CCMoveTo::actionWithDuration(?(ccTime)actualDuration,? ?? ? ? ?ccp(0?-?target->getContentSize().width/2,?actualY)?); ??CCFiniteTimeAction*?actionMoveDone?=? ?? ?CCCallFuncN::actionWithTarget(?this,? ?? ? ? ? ? ? ? ?callfuncN_selector(HelloWorld::spriteMoveFinished)); ??target->runAction(?CCSequence::actions(actionMove,? ?? ? ? ? ? ? ? ? ? ? actionMoveDone,?NULL)?); } | //?objc?with?cocos2d-iphone -(void)addTarget? { ??CCSprite?*target?=?[CCSprite?spriteWithFile:@"Target.png" ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?rect:CGRectMake(0,?0,?27,?40)]; ??//?Determine?where?to?spawn?the?target?along?the?Y?axis ??CGSize?winSize?=?[[CCDirector?sharedDirector]?winSize]; ??int?minY?=?target.contentSize.height/2; ??int?maxY?=?winSize.height?-?target.contentSize.height/2; ??int?rangeY?=?maxY?-?minY; ??int?actualY?=?(arc4random()?%?rangeY)?+?minY; ??//?Create?the?target?slightly?off-screen?along?the?right?edge, ??//?and?along?a?random?position?along?the?Y?axis?as?calculated ??target.position?=? ?? ?ccp(winSize.width?+?(target.contentSize.width/2),? ?? ?actualY); ??[self?addChild:target]; ??//?Determine?speed?of?the?target ??int?minDuration?=?2.0; ??int?maxDuration?=?4.0; ??int?rangeDuration?=?maxDuration?-?minDuration; ??int?actualDuration?=?(arc4random()?%?rangeDuration)?+?minDuration; ??//?Create?the?actions ??id?actionMove?=? ?? ?[CCMoveTo?actionWithDuration:actualDuration ?? ? ? ? ?position:ccp(-target.contentSize.width/2,?actualY)]; ??id?actionMoveDone?=? ?? ?[CCCallFuncN?actionWithTarget:self ?? ? ? ? ? ? ? ?selector:@selector(spriteMoveFinished:)]; ??[target?runAction:[CCSequence?actions:actionMove,? ?? ? ? ? ? ? ? ? ? ?actionMoveDone,?nil]];? }? |
?
這里用callfuncN_selector(HelloWorld::spriteMoveFinished)回調了spriteMoveFinished方法,我們需要實現之。同樣別忘記在頭文件里加入聲明, 然后實現之
| //?cpp?with?cocos2d-x void?HelloWorld::spriteMoveFinished(CCNode*?sender) { ??CCSprite?*sprite?=?(CCSprite?*)sender; ??this->removeChild(sprite,?true); } | //?objc?with?cocos2d-iphone -(void)spriteMoveFinished:(id)sender? { ??CCSprite?*sprite?=?(CCSprite?*)sender; ??[self?removeChild:sprite?cleanup:YES];? }? |
?
轉換要點
1. 隨機函數。在iphone上可以用arc4random()直接生成隨機函數,而uphone上還是用傳統的方法,先獲取毫秒級時間(這個函數在uphone上是TimGetTickes()),用srand(int)塞進去作為random seed,然后再調用rand()生成隨機數。其中srand和rand是C標準庫函數
?
2. objc中的YES和NO,在cpp中變成true和false,這個容易理解
?
3. 回調函數.在objc中用 selector:@selector(spriteMoveFinished),在cpp中實現就比較復雜了,具體可以看cocos2dx\include\selector_protocol.h里面的聲明??傊糠N可能出現selector的地方,都有唯一的函數指針類型與之匹配。一共有5種回調函數類型
- schedule_selector
- callfunc_selector
- callfuncN_selector
- callfuncND_selector
- menu_selector
具體使用時,可以看所用函數的變量類型定義來決定。比如使用CCTimer::initWithTarget方法,第二個參數是SEL_SCHEDULE類型,到selector_protocol.h里查一下,可以看到對應是schedule_selector(_SELECTOR)宏,所以調用時就需要在類里頭實現一個void MyClass::MyCallbackFuncName(ccTime)函數,然后把schedule_selector(MyClass::MyCallbackFuncName)作為CCTimer::initWithTarget的第二個參數傳入。
?
?
有了addTarget后,我們需要定時地調用它。所以在init函數返回前增加這個函數調用
| //?cpp?with?cocos2d-x //?Call?game?logic?about?every?second this->schedule(?schedule_selector(HelloWorld::gameLogic),?1.0?); | //?objc?with?cocos2d-iphone //?Call?game?logic?about?every?second [self?schedule:@selector(gameLogic:)?interval:1.0]; |
?
然后實現gameLogic這個回調函數
| //?cpp?with?cocos2d-x void?HelloWorld::gameLogic(ccTime?dt) { ?? ?this->addTarget(); } | //?objc?with?cocos2d-iphone -(void)gameLogic:(ccTime)dt { ?? ?[self?addTarget]; } |
?
不要忘記在頭文件里增加函數聲明,并且應為public函數,否則回調是調用不到的。
編譯運行,現在你應該看到小嘍啰們張牙舞爪地向大哥撲過來。于是拯救世界、維護人類和平的重任就交給大哥了。
本文轉自Walzer博客園博客,原文鏈接:http://www.cnblogs.com/walzer/archive/2010/10/10/1847100.html,如需轉載請自行聯系原作者
?
總結
以上是生活随笔為你收集整理的如何用cocos2d-x来开发简单的Uphone游戏:(二) 移动的精灵的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微众银行:FISCO BCOS开源生态圈
- 下一篇: IntelliJ如何设置自动导包