本节课的视频教程地址是:第三课在此
如果本教程有帮助到您,希望您能点击进去观看一下,而且现在注册成为极客学院的会员,验证手机号码和邮箱号码会赠送三天的会员时间,手机端首次也可以领取五天的会员时间哦(即使是购买年会员目前也仅仅是年费260),成为极客学院学习会员可以无限制的下载和观看所有的学院网站的视频,谢谢您的支持!
经过前面两节课的学习,我们已经知道我们要做的是一个什么样的游戏项目,并且对游戏的基本特点和其中的重难点有了一个基本的认识,并且完成了项目环境的基本搭建,以及项目基础类等工作。
从这节课开始我们就将进入到实际的逻辑功能和场景开发中来,我们将按照第一节课分析的流程和游戏实际的运行流程,将我们的整个游戏项目的所有功能和场景各个击破,并逐步连接成一个完整的游戏项目。
这节课我们要学习的内容有:
场景的UI图如下:
主开始菜单场景:
秘籍场景:
一、资源的异步加载和过渡场景的实现
这部分要学习的内容有三个方面:
- •资源打包
- •资源的异步加载
- •过渡场景的分析和实现
首先是资源的打包,这里我用的打包工具是
TexturePacker,关于此工具的具体用法,我这里不再描述,您可以查看下官方文档或者本视频教程(使用方法非常简单),如何获取和激活该工具请参考本人之前的
TexturePacker博文。
其次是关于资源的异步加载方法,这里使用的是:
•
资源
的异步
加载方法(
TextureCache
类)
voidTextureCache::addImageAsync(conststd::string &path, conststd::function<void(Texture2D*)>&callback)
这个方法的参数:
第一个是大图文件路径,第二个是一个回调函数。
下面是过渡场景的头文件代码:
/*! * \file SplashLayer.h * * \author SuooL_振生 * \date 五月 2015 * * 工作室Logo Splash界面 */ #ifndef __SplashScene__H__ #define __SplashScene__H__ #include "cocos2d.h" #include "SimpleAudioEngine.h" USING_NS_CC; class SplashLayer : public Layer { public: virtual bool init(); static Scene* createScene(); CREATE_FUNC(SplashLayer); private: Sprite* logoSprite; // 资源加载 void loadingTextureCallBack(Texture2D * texture); void loadingAudio(); // 场景切换 void nextScene(float dt); void onExit(); // 初始化用户数据 void initUserData(); int m_iNumOfLoad; std::thread* _loadingAudioThread; }; #endif
下面是实现的CPP文件代码:
/*! * \file SplashLayer.cpp * \date 2015/05/17 21:59 * * \author SuooL * Contact: hu1020935219@gmail.com * * \brief 过渡场景 * * TODO: long description * * \note */ #include "SimpleAudioEngine.h" #include "GlobalDefine.h" #include "SplashLayer.h" #include "StartLayer.h" USING_NS_CC; using namespace CocosDenshion; Scene* SplashLayer::createScene() { Scene* splashScene = Scene::create(); SplashLayer* layer = SplashLayer::create(); splashScene->addChild(layer); return splashScene; } bool SplashLayer::init() { if (!Layer::init()) { return false; } // 初始化logo精灵 logoSprite = Sprite::create("logo.png"); logoSprite->setPosition(WINSIZE.width/2, WINSIZE.height/2); this->addChild(logoSprite); // 首次运行初始化用户数据 if (!getBoolFromXML("_IS_EXISTED")) { initUserData(); setBoolToXML("_IS_EXISTED", true); UserDefault::getInstance()->flush(); } setFloatToXML(SOUNDVOL, 0.80f); setFloatToXML(MUSICVOL, 0.35f); UserDefault::getInstance()->flush(); m_iNumOfLoad = 0; // 图片和声音的异步加载 // 主界面 Director::getInstance()->getTextureCache()->addImageAsync("pnglist/startGame.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); // 图籍 Director::getInstance()->getTextureCache()->addImageAsync("pnglist/gameLayer.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); // 设置 Director::getInstance()->getTextureCache()->addImageAsync("pnglist/setLayer.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); // 秘籍 Director::getInstance()->getTextureCache()->addImageAsync("pnglist/cheatsLayer.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); // 选关 Director::getInstance()->getTextureCache()->addImageAsync("pnglist/gateMap.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); // 暂停 Director::getInstance()->getTextureCache()->addImageAsync("pnglist/pauseLayer.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); // 英雄 Director::getInstance()->getTextureCache()->addImageAsync("pnglist/hero.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("pnglist/heroComobo.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); Director::getInstance()->getTextureCache()->addImageAsync("pnglist/heroGun.png", CC_CALLBACK_1(SplashLayer::loadingTextureCallBack, this)); _loadingAudioThread = new std::thread(&SplashLayer::loadingAudio, this); return true; } void SplashLayer::loadingTextureCallBack(Texture2D * texture) { switch (m_iNumOfLoad++) { case 0: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/startGame.plist", texture); break; case 1: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/gameLayer.plist", texture); break; case 2: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/setLayer.plist", texture); break; case 3: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/cheatsLayer.plist", texture); break; case 4: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/gateMap.plist", texture); break; case 5: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/pauseLayer.plist", texture); break; case 6: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/hero.plist", texture); break; case 7: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/heroComobo.plist", texture); break; case 8: SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/heroGun.plist", texture); this->schedule(schedule_selector(SplashLayer::nextScene), 1, 1, 1); break; default: break; } } void SplashLayer::loadingAudio() { log("loadAudio"); //初始化 音乐 SimpleAudioEngine::getInstance()->preloadBackgroundMusic("Sound/startBGM.mp3"); //初始化音效 SimpleAudioEngine::getInstance()->preloadEffect("Sound/button.wav"); } void SplashLayer::initUserData() { setIntToXML(GAMELEVEL_KEY, 1); // 初始化关卡 setIntToXML(HEROENERGY_KEY, 10); // 初始化体力 setIntToXML(HEROCOIN_KEY, 1000); // 初始化金币 setBoolToXML(SOUND_KEY, true); setBoolToXML(MUSIC_KEY, true); // 刷新 UserDefault::getInstance()->flush(); } void SplashLayer::nextScene(float dt) { Director::getInstance()->replaceScene(TransitionFade::create(2.0f, StartLayer::createScene())); } void SplashLayer::onExit() { Layer::onExit(); _loadingAudioThread->join(); CC_SAFE_DELETE(_loadingAudioThread); this->unschedule(schedule_selector(SplashLayer::nextScene)); }
从上面的代码中可以看出在图片资源的预加载中,显示加载纹理大图,然后调用回调函数加载对应的plist配置文件,而在最后一个纹理完成加载之后,便会进行一个场景的切换的回调。
而声音资源的预加载则是通过多线程实现的,新开一个线程,并使用Cocos的
//初始化 音乐 SimpleAudioEngine::getInstance()->preloadBackgroundMusic("Sound/startBGM.mp3"); //初始化音效 SimpleAudioEngine::getInstance()->preloadEffect("Sound/button.wav");方法完成声音资源的预加载,在场景退出的时候注意回收线程资源。
二、Menu家族的学习和菜单场景的实现
这一部分的主要内容也是三点:
•
Menu
家族及其成员构成
•
Menu
及各个成员的特点
•
主
开始菜单场景的分析和实现
下面是菜单Menu和菜单项MenuItem类图:
他们的关系就如名字一样,一个是容器Menu,一个是内容Item。
下面引用一段话:
Scene* StartLayer::createScene() { Scene* startScene = Scene::create(); StartLayer* layer = StartLayer::create(); startScene->addChild(layer); return startScene; } bool StartLayer::init() { if (!Layer::init()) { return false; } // 加载游戏图片资源缓存 SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/galleryLayer.plist"); SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/monster.plist"); SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/resultLayer.plist"); SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/mapBg.plist"); SpriteFrameCache::getInstance()->addSpriteFramesWithFile("pnglist/mapMid.plist"); // 根据音乐的开关来播放背景音乐 if (getBoolFromXML(MUSIC_KEY)) { float music = getFloatFromXML(MUSICVOL)*100.0f; aduioEngine->setBackgroundMusicVolume(getFloatFromXML(MUSICVOL)); if (SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { aduioEngine->pauseBackgroundMusic(); aduioEngine->playBackgroundMusic("Sound/startBGM.mp3", true); } else aduioEngine->playBackgroundMusic("Sound/startBGM.mp3", true); } else aduioEngine->pauseBackgroundMusic(); // 精灵初始化及位置设定 title = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("Title.png")); title->setPosition(WINSIZE.width / 2 - 222, WINSIZE.height / 2 + 186); bgPic = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("MainMenuBackground.png")); bgPic->setPosition(WINSIZE.width / 2, WINSIZE.height / 2); this->addChild(bgPic); this->addChild(title); // 按钮初始化以及时间绑定 auto helpItem = MenuItemSprite::create( Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("HelpNormal.png")), Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("HelpSelected.png")), CC_CALLBACK_1(StartLayer::touchHelp, this)); // 帮助 auto tujiItem = MenuItemSprite::create( Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("PhotoGalleryNormal.png")), Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("PhotoGallerySelected.png")), CC_CALLBACK_1(StartLayer::touchLib, this)); // 图籍 auto setItem = MenuItemSprite::create( Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("SetNormal.png")), Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("SetSelected.png")), CC_CALLBACK_1(StartLayer::touchSet, this)); // 设置 auto mijiItem = MenuItemSprite::create( Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("CheatsNormal.png")), Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("CheatsSelected.png")), CC_CALLBACK_1(StartLayer::touchMiJi, this)); // 秘籍 auto chuangguanItem = MenuItemSprite::create( Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("EmigratedNormal.png")), Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("EmigratedSelected.png")), CC_CALLBACK_1(StartLayer::touchCG, this)); // 闯关 auto tiaozhanItem = MenuItemSprite::create( Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("ChallengeNormal.png")), Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("ChallengeSelected.png")), CC_CALLBACK_1(StartLayer::touchTZ, this)); // 挑战 tujiItem->setPosition(WINSIZE.width - 62, WINSIZE.height - 73); mijiItem->setPosition(WINSIZE.width - 62, WINSIZE.height - 209); setItem->setPosition(WINSIZE.width - 62, WINSIZE.height - 346); helpItem->setPosition(WINSIZE.width - 62, WINSIZE.height - 473); chuangguanItem->setPosition(WINSIZE.width / 2 - 240, WINSIZE.height / 2 - 86); tiaozhanItem->setPosition(WINSIZE.width / 2 - 240, WINSIZE.height / 2 - 250); auto menu = Menu::create(tujiItem,mijiItem, setItem, helpItem, chuangguanItem, tiaozhanItem, NULL); menu->setPosition(Point::ZERO); this->addChild(menu, 2); return true; } // 按钮事件实现 void StartLayer::touchSet(Ref* pSender) { PLAYEFFECT; Director::getInstance()->replaceScene(SetLayer::createScene()); } void StartLayer::touchLib(Ref* pSender) { PLAYEFFECT; Director::getInstance()->replaceScene(TujiLayer::createScene()); } void StartLayer::touchMiJi(Ref* pSender) { PLAYEFFECT; Director::getInstance()->replaceScene(MijiLayer::createScene()); } void StartLayer::touchCG(Ref* pSender) { if (getBoolFromXML(SOUND_KEY)) { aduioEngine->setEffectsVolume(getFloatFromXML(SOUNDVOL)); aduioEngine->playEffect("Sound/button.mp3"); } Director::getInstance()->replaceScene(GateMapLayer::createScene()); } void StartLayer::touchTZ(Ref* pSender) { PLAYEFFECT; // Director::getInstance()->replaceScene(GateMapLayer::createScene()); } void StartLayer::touchHelp(Ref* pSender) { PLAYEFFECT; Director::getInstance()->replaceScene(HelpLayer::createScene()); }
三、秘籍场景的实现
秘籍场景是一个很简单的场景,效果图如上所示,其主要的一个逻辑就是点击两边的切换选项,或切换显示不同的图片。非常简单。
这里我直接贴出关键代码:
Scene* MijiLayer::createScene() { Scene* scene = Scene::create(); MijiLayer* layer = MijiLayer::create(); scene->addChild(layer); return scene; } bool MijiLayer::init() { if (!Layer::init()) { return false; } flag = true; // 背景 spriteBG = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("CheatsBackground.png")); spriteBG->setPosition(WINSIZE.width / 2, WINSIZE.height / 2); this->addChild(spriteBG); // 秘籍技能界面 interface_1 = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("CheatsInterface1.png")); interface_2 = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("CheatsInterface2.png")); interface_1->setPosition(WINSIZE.width / 2, WINSIZE.height / 2 - 10); interface_1->setVisible(true); interface_2->setPosition(WINSIZE.width / 2, WINSIZE.height / 2 - 10); interface_2->setVisible(false); spriteBG->addChild(interface_1); spriteBG->addChild(interface_2); // 关闭按钮 auto closeItem = MenuItemSprite::create( Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("OffNormal.png")), Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("offSelected.png")), [](Ref * ref){ // 切换主界面场景 PLAYEFFECT; Director::getInstance()->replaceScene(StartLayer::createScene()); }); closeItem->setPosition(WINSIZE.width-164, WINSIZE.height-132); // 点击切换按钮 auto nextRightItem = MenuItemSprite::create( Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("PageTurnNormal.png")), Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("PageTurnSelected.png")), [&](Ref * ref){ PLAYEFFECT; // 切换秘籍 if (flag) { interface_2->setVisible(true); flag = false; } else { interface_2->setVisible(false); flag = true; } }); nextRightItem->setPosition(WINSIZE.width - 55, WINSIZE.height / 2 - 14); // 点击切换按钮 auto nor = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("PageTurnNormal.png")); nor->setFlippedX(true); auto sel = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("PageTurnSelected.png")); sel->setFlippedX(true); auto nextLeftItem = MenuItemSprite::create(nor, sel, [&](Ref * ref){ PLAYEFFECT; // 切换秘籍 if (flag) { interface_2->setVisible(true); flag = false; } else { interface_2->setVisible(false); flag = true; } }); nextLeftItem->setPosition(55, WINSIZE.height / 2 - 14); auto menu = Menu::create(closeItem, nextRightItem, nextLeftItem, NULL); menu->setPosition(Point::ZERO); spriteBG->addChild(menu); return true; }
这就是本节课的主要内容,主要就是一个资源的预加载学习和Menu家族组件的学习。
转载请注明出处,谢谢合作!
本节课的视频教程地址是:第三课在此
如果本教程有帮助到您,希望您能点击进去观看一下,而且现在注册成为极客学院的会员,验证手机号码和邮箱号码会赠送三天的会员时间,手机端首次也可以领取五天的会员时间哦(即使是购买年会员目前也仅仅是年费260),成为极客学院学习会员可以无限制的下载和观看所有的学院网站的视频,谢谢您的支持!