Cocos2d-x學習(十一):用cocos2d-x實現MoonWarriors(遊戲場景切換和主菜單實現)

特別聲明:本文所有圖片資源來源於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);



項目地址










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章