特別聲明:本文所有圖片資源來源於MoonWarriors cocos2d-html 開源項目
在cocos2d-html alpha版推出不久,就有一位大神寫了一款飛行射擊類遊戲,MoonWarrior,MoonWarrior cocos2d-html github地址,是用js寫的,代碼寫的很清晰,雖然不是很懂js,但是都是基於cocos2d架構的,所以還是可以看的懂的,加上最近有看了兩本cocos2d的書(《iphone & ipad cocos2d 遊戲開發實戰》和《Learning Coocs2d》),決定用cocos2d-x重寫一遍這個遊戲demo,並加入一些從以上兩本書中學到的知識以及自己的認識!
1.遊戲管理器
在《Learning Cocos2d》這本書中提到了GameManager遊戲管理器這個概念,設計模式採用的是單例,其重要用途是管理遊戲中的一些全局的屬性和操作,比如背景音樂,音效的播放,場景的切換,遊戲的設置等等,這種單例設計模式的管理器的好處很多,所以我也在我的項目中加入了這一設計,今天先簡單總結一下關於場景切換的實現,GameManager類的定義如下:
class GameManager
{
private:
GameManager();
// 初始化數據
void init();
public:
~GameManager();
public:
static GameManager* sharedGameManager();
public:
// 跳轉場景
void runSceneWithID(SCENE_ID pSceneID);
// 當前場景
CC_SYNTHESIZE_READONLY(SCENE_ID, mCurrentScene, CurrentScene);
};
切換場景的方法void GameManager::runSceneWithID(SCENE_ID pSceneID)
{
SCENE_ID oldSceneID = mCurrentScene;
mCurrentScene = pSceneID;
CCScene *sceneToRun = NULL;
switch (mCurrentScene) {
case SCENE_ID_MAINMENU:
sceneToRun = MainMenuScene::node();
break;
case SCENE_ID_GAMEPLAY:
sceneToRun = GamePlayScene::node();
break;
case SCENE_ID_SETTING:
sceneToRun = SettingScene::node();
break;
case SCENE_ID_ABOUT:
sceneToRun = AboutScene::node();
break;
default:
// 異常
CCAssert(0, "Unknown Scene ID");
break;
}
if (!sceneToRun) {
mCurrentScene = oldSceneID;
return;
}
// 判斷是否是第一次載入場景
if (!CCDirector::sharedDirector()->getRunningScene()) {
CCDirector::sharedDirector()->runWithScene(sceneToRun);
}
else {
CCDirector::sharedDirector()->replaceScene(sceneToRun);
}
}
首先定義一個區分不同場景的枚舉ID
// Scene ID
typedef enum
{
//
SCENE_ID_NONE = -1,
// 主菜單場景
SCENE_ID_MAINMENU = 0,
// 遊戲場景
SCENE_ID_GAMEPLAY = 1,
// 遊戲設置場景
SCENE_ID_SETTING = 2,
// 關於場景
SCENE_ID_ABOUT = 3
} SCENE_ID;
然後根據切換場景傳入的不同場景ID值,切換到不同的場景
這裏需要注意的是,第一次載入場景時需要調用
CCDirector::sharedDirector()->runWithScene(sceneToRun);
可以通過
CCDirector::sharedDirector()->getRunningScene()
來判斷當前是否有正在運行的場景,如果沒有,則是第一次載入了!
這樣設計的好處在於不論在程序的哪個地方,只需要一個接口,並且傳遞要跳轉場景的ID值即可,用法如下
GameManager::sharedGameManager()->runSceneWithID(SCENE_ID_MAINMENU);
2.主菜單的實現
主菜單場景我分爲了兩層,第一層是背景層,第二層是菜單層,效果如圖
(1)背景層
背景層是有底圖,Logo和一個移動的飛船組成的
底圖和Logo很簡單,只是兩個圖片紋理的精靈而已
爲了效果更好,MoonWarriors提供了一個移動的飛船,我也山寨過來了
這裏我用到CCImage和CCTexture2D類,我沒有像原作那樣將飛船的圖片資源添加到CCTextureCache中,因爲我覺得在其他場景中這個圖片資源可能是沒有用的,添加到CCTextureCache中是不是沒有必要!代碼如下:
// 動畫飛船
// CCImage是由不同平臺不同實現的
CCImage shipImage;
shipImage.initWithImageFile("ship.png");
CCTexture2D *shipTexture = new CCTexture2D();
shipTexture->initWithImage(&shipImage);
CCSprite *shipSprite = CCSprite::spriteWithTexture(shipTexture, CCRectMake(0, 45, 60, 38));
shipTexture->release();
// 設置在屏幕之外
shipSprite->setPosition(ccp(-shipSprite->getContentSize().width, -shipSprite->getContentSize().height));
this->addChild(shipSprite);
飛船移動的動作也很簡單
void MainMenuBGLayer::runShipAnim(cocos2d::CCNode *pShip)
{
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
pShip->setPosition(ccp(CCRANDOM_0_1() * winSize.width, 0));
// 從底部移動到頂部,執行回調繼續執行移動動作
pShip->runAction(CCSequence::actions(CCMoveTo::actionWithDuration(2, ccp(CCRANDOM_0_1() * winSize.width, winSize.height + pShip->getContentSize().height / 2)), CCCallFunc::actionWithTarget(this, callfunc_selector(MainMenuBGLayer::spriteAnimCallback)), NULL));
}
這裏的位置座標沒什麼好糾結的,只是取了一個隨機數的寬度而已!
(2)菜單層
菜單項使用的是CCMenuItemSprite,這個通常是用在菜單項選中,未選中已經不可選的狀態下顯示不同圖片的
原作中的菜單圖片資源是做在一起的,所以用到了一些座標,代碼如下
// 菜單
// 菜單紋理
CCImage menuImage;
menuImage.initWithImageFile("menu.png");
CCTexture2D *menuTexture = new CCTexture2D();
menuTexture->initWithImage(&menuImage);
// 菜單選項
CCMenuItemSprite *newGameMenuItem = CCMenuItemSprite::itemFromNormalSprite(CCSprite::spriteWithTexture(menuTexture, CCRectMake(0, 0, 126, 33)), CCSprite::spriteWithTexture(menuTexture, CCRectMake(0, 33, 126, 33)), CCSprite::spriteWithTexture(menuTexture, CCRectMake(0, 33 * 2, 126, 33)), this, menu_selector(MainMenuCtrlLayer::mainMenuCallback));
newGameMenuItem->setTag(MAINMENU_ID_NEW_GAME);
CCMenuItemSprite *settingMenuItem = CCMenuItemSprite::itemFromNormalSprite(CCSprite::spriteWithTexture(menuTexture, CCRectMake(126, 0, 126, 33)), CCSprite::spriteWithTexture(menuTexture, CCRectMake(126, 33, 126, 33)), CCSprite::spriteWithTexture(menuTexture, CCRectMake(126, 33 * 2, 126, 33)), this, menu_selector(MainMenuCtrlLayer::mainMenuCallback));
settingMenuItem->setTag(MAINMENU_ID_SETTING);
CCMenuItemSprite *aboutMenuItem = CCMenuItemSprite::itemFromNormalSprite(CCSprite::spriteWithTexture(menuTexture, CCRectMake(252, 0, 126, 33)), CCSprite::spriteWithTexture(menuTexture, CCRectMake(252, 33, 126, 33)), CCSprite::spriteWithTexture(menuTexture, CCRectMake(252, 33 * 2, 126, 33)), this, menu_selector(MainMenuCtrlLayer::mainMenuCallback));
aboutMenuItem->setTag(MAINMENU_ID_ABOUT);
CCMenu *mainMenu = CCMenu::menuWithItems(newGameMenuItem, settingMenuItem, aboutMenuItem, NULL);
mainMenu->alignItemsVerticallyWithPadding(10);
mainMenu->setPosition(ccp(winSize.width / 2, winSize.height / 2 - 80));
this->addChild(mainMenu);