本文参考LT大树_的博客文章《cocos2d-x 3.2 之 三消类游戏》素材也是使用的LT大树_提供的,我只增加了四个按钮的图片。如有您感觉这篇文章侵犯了您的权益,请联系我,我将马上删除!
所需图片和音乐这里下载http://pan.baidu.com/s/1jGMnbPO 其中几个图是我自己p的,可能有点违和感,凑合用吧
首先我的环境:win8.1 64bit + VS2012专业版 + Cocos2d-X3.4
第一步当然是首先新建项目啦,在你存放项目的目录摁住shift键然后点击鼠标右键,选择“在此处打开命令行”。当然你也可以直接运行命令提示符在创建项目命令里边制定存放目录。
cocos new MyHalloweenFight -l cpp -p com.halloweenfight.lei -d .new 后边跟的是项目名称,-l 后边是语言,-p 后边是包名,-d 后边是项目存放目录,因为我习惯直接在项目的存放目录执行命令,所以就用 . 代替(. 一个点代表当前目录)(.. 两个点代表上一层目录)
项目创建完成之后就要进行第一次编译了,其实不用打开vs,还在当前的命令行,直接cd到项目目录里边,执行命令
cocos compile -p win32是不是很熟悉?跟编译安卓是一样的,只不过把-p后边的参数由android换成了win32。个人感觉这样可能比打开vs编译要稍微快一些,毕竟一个小黑窗口比vs那个窗口占用的资源要少。等待大概三四十分钟(好吧,我承认我的电脑比较慢,不过对于一个服役5年的老神舟笔记本要求也不能太高了。你们新的i5、i7估计用不了10分钟吧)
OK,下面就要进入正题了。
首先调整一下分辨率和窗口的名字,打开AppDelegate.cpp
bool AppDelegate::applicationDidFinishLaunching() { auto director = Director::getInstance(); auto glview = director->getOpenGLView(); if(!glview) { //设置窗口显示的名字,不改也行,没有任何影响 glview = GLViewImpl::create("Halloween Fight"); //设置窗口大小,模拟手机屏幕的分辨率 glview->setFrameSize(480,800); director->setOpenGLView(glview); } //设置设计尺寸,运行时会根据上边设置的窗口大小或者手机屏幕大小和第三个参数对游戏整体缩放或拉伸 glview->setDesignResolutionSize(480,800,ResolutionPolicy::EXACT_FIT); director->setDisplayStats(true); director->setAnimationInterval(1.0 / 60); //WelcomeScene是新建的一个类,用来显示进入游戏第一屏画面。当然也可以直接在原来的HelloWorldScene的基础上修改,不过那样不就显得不专业了么 auto scene = WelcomeScene::createScene(); director->runWithScene(scene); return true; }
然后我们就要开始写WelcomeScene这个类了,这个界面要有一个背景,一个开始按钮,一个退出按钮,一个音乐开关按钮,一个音效开关按钮。
WelcomeScene.h
#ifndef __WELCOMESCENE_H__ #define __WELCOMESCENE_H__ #include "cocos2d.h" using namespace cocos2d; class WelcomeScene : public Layer { public : /*****一些初始化函数*****/ static Scene* createScene(); //create函数也可以用CREATE_FUNC(WelcomeScene)代替,不过我喜欢自己打,熟悉一下代码嘛 static WelcomeScene* create(); //create函数自动调用该函数 virtual bool init(); //过场动画执行结束时调用该函数 virtual void onEnterTransitionDidFinish(); //退出场景时执行该函数 virtual void cleanup(); bool musicStatus; bool soundStatus; /*****按钮的回调函数*****/ //开始按钮 void menuStartCallback(Ref* pSender); //退出按钮 void menuExitCallback(Ref* pSender); //音效开关按钮 void soundToggleCallback(Ref* pSender); //音乐开关按钮 void musicToggleCallback(Ref* pSender); }; #endif //__WELCOMESCENE_H__两个bool值分别表示音乐状态和音效状态,这个具体怎么用后边会说到。
对了,还需要新建一个头文件,用来存放一些宏定义什么的,我习惯把这个头文件命名为def.h
#ifndef __DEF_H__ #define __DEF_H__ #define MUSIC_KEY "music_key" #define SOUND_KEY "sound_key" #endif // !__DEF_H__这4个宏定义都是后边开关按钮要用到的
头文件写好了接下来就是对这些函数进行实现了,打开WelcomeScene.cpp
#include "WelcomeScene.h" #include "SimpleAudioEngine.h" #include "def.h" Scene* WelcomeScene::createScene() { auto scene = Scene::create(); auto layer = WelcomeScene::create(); scene->addChild(layer); return scene; } //如果头文件里使用了CRATE_FUNC(WelcomeScene)这个函数就不需要实现了 WelcomeScene* WelcomeScene::create() { auto ref = new WelcomeScene(); if(ref && ref->init()) { ref->autorelease(); return ref; } else { delete ref; ref = NULL; return NULL; } } bool WelcomeScene::init() { if(!Layer::init()) return false; //对父类进行初始化 //UserDefault是cocos2d-x提供的一个简单的数据持久化的工具 auto userDefault = UserDefault::getInstance(); //获取一个UserDefault的实例 //musicStatus = userDefault->getBoolForKey(MUSIC_STATUS,true); //获取键名为MUSIC_STATUS(def.h里边定义的)的值,如果该键不存在则返回true //soundStatus = userDefault->getBoolForKey(SOUND_STATUS,true); //同上 //if(musicStatus != userDefault->getBoolForKey(MUSIC_KEY,true)) //获取键名为MUSIC_KEY(def.h里定义)的值,如果该键不存在则返回true,并与上边的musicStatus比较。 //{ // userDefault->setBoolForKey(MUSIC_KEY,musicStatus); //} //if(soundStatus != userDefault->getBoolForKey(SOUND_KEY,true)) //{ // userDefault->setBoolForKey(SOUND_KEY,soundStatus); //} //如果MUSIC_KEY不存在就说明是第一次运行,返回3,并将MUSIC_KEY的值设置为0 if(userDefault->getIntegerForKey(MUSIC_KEY,3) == 3) { userDefault->setIntegerForKey(MUSIC_KEY,0); } if(userDefault->getIntegerForKey(SOUND_KEY,3) == 3) { userDefault->setIntegerForKey(SOUND_KEY,0); } //对音乐文件进行预处理,防止使用时卡顿 auto audioEng = CocosDenshion::SimpleAudioEngine::getInstance(); audioEng->preloadBackgroundMusic("music/music_bg.mp3"); audioEng->preloadBackgroundMusic("music/music_mainScene.mp3"); audioEng->preloadEffect("music/music_explode.wav"); audioEng->preloadEffect("music/music/explodeOne.wav"); audioEng->preloadEffect("music/music_fail.mp3"); audioEng->preloadEffect("music/music/gameOver.mp3"); audioEng->preloadEffect("music/music_win.wav"); //获取屏幕可视尺寸 auto visibleSize = Director::getInstance()->getVisibleSize(); //添加背景图片 auto bg = Sprite::create("scene_sta.png"); bg->setPosition(visibleSize / 2); this->addChild(bg); //开始按钮 auto startBtn = MenuItemImage::create("btn_start01.png","btn_start02.png",CC_CALLBACK_1(WelcomeScene::menuStartCallback,this)); startBtn->setPosition(visibleSize.width / 2,visibleSize.height / 4); //退出按钮 auto exitBtn = MenuItemImage::create("btn_exit.png","btn_exit.png",CC_CALLBACK_1(WelcomeScene::menuExitCallback,this)); exitBtn->setPosition(visibleSize.width - exitBtn->getContentSize().width,exitBtn->getContentSize().height); //音乐开关按钮 auto musicOnImg = MenuItemImage::create("music_on.png","music_on.png"); auto musicOffImg = MenuItemImage::create("music_off.png","music_off.png"); //最后记得加NULL,否则会报错 auto musicToggleBtn = MenuItemToggle::createWithCallback(CC_CALLBACK_1(WelcomeScene::musicToggleCallback,this),musicOnImg,musicOffImg,NULL); //三目运算符,获取MUSIC_KEY的值,如果为true则执行冒号前边的语句将将该按钮显示的图片设置为第0张即music_on.png,如果为false则执行冒号后边的语句将该按钮显示的图片设置为第1张即music_off.png //userDefault->getBoolForKey(MUSIC_KEY,true) ? musicToggleBtn->setSelectedIndex(0) : musicToggleBtn->setSelectedIndex(1); userDefault->getIntegerForKey(SOUND_KEY) == 0 ? soundToggleBtn->setSelectedIndex(0) : soundToggleBtn->setSelectedIndex(1); musicToggleBtn->setPosition(visibleSize.width - exitBtn->getContentSize().width * 2,exitBtn->getContentSize().height); //音效开关按钮 auto soundOnImg = MenuItemImage::create("sound_on.png","sound_on.png"); auto soundOffImg = MenuItemImage::create("sound_off.png","sound_off.png"); //最后记得加NULL,否则会报错 auto soundToggleBtn = MenuItemToggle::createWithCallback(CC_CALLBACK_1(WelcomeScene::soundToggleCallback,soundOnImg,soundOffImg,NULL); //同音乐开关按钮 //userDefault->getBoolForKey(SOUND_KEY,true) ? soundToggleBtn->setSelectedIndex(0) : soundToggleBtn->setSelectedIndex(1); userDefault->getIntegerForKey(SOUND_KEY) == 0 ? soundToggleBtn->setSelectedIndex(0) : soundToggleBtn->setSelectedIndex(1); soundToggleBtn->setPosition(visibleSize.width - exitBtn->getContentSize().width * 3,exitBtn->getContentSize().height); //创建菜单容器 最后记得加NULL,否则会报错 auto menu = Menu::create(startBtn,exitBtn,musicToggleBtn,soundToggleBtn,NULL); menu->setPosition(Vec2::ZERO); this->addChild(menu); return true; } void WelcomeScene::onEnterTransitionDidFinish() { //获取MUSIC_KEY的值,如果不存在返回true。所以第一次运行肯定为true,也就是默认播放背景音乐 if(UserDefault::getInstance()->getBoolForKey(MUSIC_KEY,true)) { CocosDenshion::SimpleAudioEngine::getInstance()->playBackgroundMusic("music/music_bg.mp3",true); } } void WelcomeScene::cleanup() { //退出场景是如果背景音乐正在播放,则停止播放 if (CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->stopBackgroundMusic(true); } } //开始按钮的回调函数,因为还没有写游戏界面,所以这里先空着 void WelcomeScene::menuStartCallback(Ref* pSender) { } //退出按钮的回调函数 void WelcomeScene::menuExitCallback(Ref* pSender) { Director::getInstance()->end(); } //音乐开关按钮的回调函数 void WelcomeScene::musicToggleCallback(Ref* pSender) { //如果背景音乐正在播放,则暂停播放 if(CocosDenshion::SimpleAudioEngine::getInstance()->isBackgroundMusicPlaying()) { CocosDenshion::SimpleAudioEngine::getInstance()->pauseBackgroundMusic(); } //如果背景音乐没有播放,则开始播放 else { CocosDenshion::SimpleAudioEngine::getInstance()->resumeBackgroundMusic(); } //点击按钮时如果musicStatus的值为true则改为false,如果为false则改为true,并将其写入文件 //musicStatus = !musicStatus; //UserDefault::getInstance()->setBoolForKey(MUSIC_STATUS,musicStatus); auto userDefault = UserDefault::getInstance(); //三目运算符,如果MUSIC_KEY的值是0就改为1,否则设置为0 userDefault->getIntegerForKey(MUSIC_KEY) == 0 ? userDefault->setIntegerForKey(MUSIC_KEY,1) : userDefault->setIntegerForKey(MUSIC_KEY,0); } //音效开关按钮的回调函数 void WelcomeScene::soundToggleCallback(Ref* pSender) { //点击按钮时如果soundStatus的值为true则改为false,如果为false则改为true,并将其写入文件 //soundStatus = !soundStatus; //UserDefault::getInstance()->setBoolForKey(SOUND_STATUS,soundStatus); auto userDefault = UserDefault::getInstance(); userDefault->getIntegerForKey(SOUND_KEY) == 0 ? userDefault->setIntegerForKey(SOUND_KEY,1) : userDefault->setIntegerForKey(SOUND_KEY,0); }
再说一下开关按钮的实现逻辑吧,以音乐开关按钮为例,用两个值(MUSIC_KEY和MUSIC_STATUS)来控制,点击按钮是改变的是MUSIC_STATUS的值并且实时写入到文件,所以MUSIC_STATUS的值表示的就是当前的设置。每次运行时检查两个值是否相同,如果相同就不采取操作,如果不同则将MUSIC_KEY的值改为MUSIC_STATUS的值。当需要播放音乐时只需要获取MUSIC_KEY的值来判断就可以了。一开始是只是用MUSIC_KEY来控制,但是每次运行游戏都会将这个值初始化为true,这也算是UserDefault的一个缺点吧,不能检测这个键是否存在
刚才写上边一段话的时候突然想到为什么MUSIC_KEY的值非得用bool类型呢,用个int值不也行吗,0表示有音乐,1表示没有音乐,如果MUSIC_KEY不存在就返回3(之所以用3是因为当时脑抽了一下把2落下了)。于是在本机上试了一下果然没问题,就上来修改文章,原来的代码只是注释掉了,有兴趣的话可以研究一下我当时的奇葩思维。
另外还需要说明的一点是,在windows平台执行了pauseBackgroundMusic()之后背景音乐会停止,但是执行resumeBackgroundMusic()却不会恢复播放。这是因为我们在按钮的回掉函数里边检查了isPlayingBackgroundMusic()这个值,根据这个值来判断是暂停音乐还是恢复音乐,而这个值在windows下一直为true,所以点击按钮就只会执行pauseBackgroundMusic()这句话。不过在android没有这个问题。
如果一切顺利的话就可以看到游戏界面了,就像这样