特别声明:本文所有图片资源来源于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类的定义如下:
- <span style="font-size:16px;">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);
- };</span>
- <span style="font-size:16px;">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);
- }
- }</span>
- <span style="font-size:16px;">// 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;</span>
这里需要注意的是,第一次载入场景时需要调用
- <span style="font-size:16px;">CCDirector::sharedDirector()->runWithScene(sceneToRun);</span>
- <span style="font-size:16px;">CCDirector::sharedDirector()->getRunningScene()</span>
这样设计的好处在于不论在程序的哪个地方,只需要一个接口,并且传递要跳转场景的ID值即可,用法如下
- <span style="font-size:16px;">GameManager::sharedGameManager()->runSceneWithID(SCENE_ID_MAINMENU);</span>
2.主菜单的实现
主菜单场景我分为了两层,第一层是背景层,第二层是菜单层,效果如图
(1)背景层
背景层是有底图,Logo和一个移动的飞船组成的
底图和Logo很简单,只是两个图片纹理的精灵而已
为了效果更好,MoonWarriors提供了一个移动的飞船,我也山寨过来了
这里我用到CCImage和CCTexture2D类,我没有像原作那样将飞船的图片资源添加到CCTextureCache中,因为我觉得在其他场景中这个图片资源可能是没有用的,添加到CCTextureCache中是不是没有必要!代码如下:
- <span style="font-size:16px;">// 动画飞船
- // 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);</span>
- <span style="font-size:16px;">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));
- }</span>
(2)菜单层
菜单项使用的是CCMenuItemSprite,这个通常是用在菜单项选中,未选中已经不可选的状态下显示不同图片的
原作中的菜单图片资源是做在一起的,所以用到了一些坐标,代码如下
- <span style="font-size:16px;">// 菜单
- // 菜单纹理
- 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);</span>