~~~~我的生活,我的点点滴滴!!
玩游戏的时候,背景音乐和音效能够使一款游戏增添不少色彩。
背景音乐:时间相对比较长,同一时间内只能播放一首背景音乐。
音效:时间相对比较短,同一时间内可以播放多个音效。
不管是音乐还是音效,cocos2d-x中都采用SimpleAudioEngine类实现跨平台的声音引擎。
今天我们分析的例子就是cpp-tests中关于声音的引擎使用例子部分。
主要平台 |
支持类型 |
Android |
android.media.MediaPlayer类支持的格式,包括MP3、WAV和3GP |
iOS |
cocos2d-iPhone中cocosDenshion支持的类型,推荐MP3和CAF |
Win32 |
MID和WAV |
函数名 |
返回类型 |
描述 |
preloadBackgroundMusic |
空 |
预加载背景音乐 |
playBackgroundMusic |
空 |
播放背景音乐 |
stopBackgroundMusic |
空 |
停止背景音乐 |
pauseBackgroundMusic |
空 |
暂停背景音乐 |
resumeBackgroundMusic |
空 |
重新开始背景音乐 |
rewindBackgroundMusic |
空 |
回放背景音乐 |
willPlayBackgroundMusic |
布尔型 |
是否会播放背景音乐 |
isBackgroundMusicPlaying |
布尔型 |
是否正播放背景音乐 |
getBackgroundMusicVolume |
布尔型 |
获得背景音乐音量 |
setBackgroundMusicVolume |
空 |
设置背景音乐音量 |
平台名称 |
支持类型 |
Android |
对应OGG格式支持最好,同时支持WAV格式 |
iOS |
cocos2d-iPhone中cocosDenshion支持的类型,推荐CAF |
Win32 |
MID和WAV |
函数名 |
返回类型 |
描述 |
getEffectsVolume |
浮点型 |
获得音效音量 |
setEffectsVolume |
空 |
设置音效音量 |
playEffect |
整型 |
播放音效,参数为文件路径和是否循环 |
pauseEffect |
空 |
暂停音效,参数为播放时获得的ID号 |
pauseAllEffects |
空 |
暂停所有音效 |
resumeEffect |
空 |
开始音效,参数为播放时获得的ID号 |
resumeAllEffects |
空 |
开始所有音效 |
stopEffect |
空 |
停止音效,参数为播放时获得的ID号 |
stopAllEffects |
空 |
停止所有音效 |
preloadEffect |
空 |
预加载音效 |
unloadEffect |
空 |
将预加载的音效从缓存中删除 |
//注意要带上此头文件,他是主要的声音引擎类,不同平台有不同的接口,但是类名还是一样的 #include "SimpleAudioEngine.h" #include "extensions/GUI/CCControlExtension/CCControlSlider.h" //================================================================= //对于不同平台支持不同的音乐与音效格式,这点很重要,上面的表格已经有说到 //也许不全,但是肯定是主要的,下面的官方例子也有讲解 //================================================================= // android effect only support ogg #if (CC_TARGET_PLATFORM == CC_PLATFOR_ANDROID) #define EFFECT_FILE "effect2.ogg" #elif( CC_TARGET_PLATFORM == CC_PLATFOR_MARMALADE) #define EFFECT_FILE "effect1.raw" #else #define EFFECT_FILE "effect1.wav" #endif // CC_PLATFOR_ANDROID #if (CC_TARGET_PLATFORM == CC_PLATFOR_WIN32) #define MUSIC_FILE "music.mid" #elif (CC_TARGET_PLATFORM == CC_PLATFOR_BLACKBERRY || CC_TARGET_PLATFORM == CC_PLATFOR_LINUX ) #define MUSIC_FILE "background.ogg" #else #define MUSIC_FILE "background.mp3" #endif // CC_PLATFOR_WIN32 USING_NS_CC; using namespace CocosDenshion; #define LINE_SPACE 40 class Button : public Node//, public TargetedTouchDelegate { public: //此处省略N行代码 ...... ~Button() { // Director::getInstance()->getTouchDispatcher()->removeDelegate(this); } //后面lambda表达式给std::function<void()> &onTriggered赋值,这是一个回调功能函数 //但是我没有发现什么触发的这个回调,所谓回调就是提前写好在哪里调用,仔细看效果 //是在鼠标点击结束后才会相应的动作产生,这样我们就去看onTouchEnded()这个函数, //他在里面有个调用_onTriggered()这个动作,他就是我们绑定的回调函数。 //至于onTouchEnded()为什么能触发,那是cocos2dx的消息 //机制推动的,这个大家要深究可以去看源码了,我这里只是解释为什么这样设置了回调 //函数后就能触发我们想要的效果,而不是cocos2dx在促使回调进行 void onTriggered(const std::function<void()> &onTriggered) { _onTriggered = onTriggered; } private: Button() : _child(NULL) { // 注册触摸事件 auto listener = EventListenerTouchOneByOne::create(); listener->setSwallowTouches(true); //不懂的可以去看链接http://blog.csdn.net/ac_huang/article/details/37839413 listener->onTouchBegan = CC_CALLBACK_2(Button::onTouchBegan, this); listener->onTouchEnded = CC_CALLBACK_2(Button::onTouchEnded, this); listener->onTouchCancelled = CC_CALLBACK_2(Button::onTouchCancelled, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); } //此处省略N行代码 ...... //返回触摸区域 bool touchHits(Touch *touch) { const Rect area(0, 0, _child->getContentSize().width, _child->getContentSize().height); return area.containsPoint(_child->convertToNodeSpace(touch->getLocation())); } bool onTouchBegan(Touch *touch, Event *event) { CC_UNUSED_PARAM(event); const bool hits = touchHits(touch); if (hits) //产生缩小,看上去点击的感觉 scaleButtonTo(0.9f); return hits; } void onTouchEnded(Touch *touch, Event *event) { CC_UNUSED_PARAM(event); const bool hits = touchHits(touch); if (hits && _onTriggered) //在这里进度回调的 _onTriggered(); scaleButtonTo(1); } void onTouchCancelled(Touch *touch, Event *event) { CC_UNUSED_PARAM(event); scaleButtonTo(1); } void scaleButtonTo(float scale) { auto action = ScaleTo::create(0.1f, scale); action->setTag(900); //个人觉得stopActionByTag()函数能把所有相同的tag的Action动作都停止掉 //后来看完代码,确实也是如此 stopActionByTag(900); runAction(action); } Node *_child; std::function<void()> _onTriggered; }; class AudioSlider : public Node { public: //此处省略N行代码 ...... private: AudioSlider(Direction direction) : _direction(direction) , _slider(NULL) , _lblMinValue(NULL) , _lblMaxValue(NULL) { } bool init() { //建议去看ControlSlider的源码,了解此类的一些基本接口,此类用来设置滑动条 _slider = extension::ControlSlider::create("extensions/sliderTrack.png","extensions/sliderProgress.png" ,"extensions/sliderThumb.png"); //设置缩放为原来的一半 _slider->setScale(0.5); if (_direction == Vertical) _slider->setRotation(-90.0); addChild(_slider); return true; } Direction _direction; extension::ControlSlider *_slider; LabelTTF *_lblMinValue; LabelTTF *_lblMaxValue; }; CocosDenshionTest::CocosDenshionTest() : _soundId(0), _musicVolume(1), _effectsVolume(1), _sliderPitch(NULL), _sliderPan(NULL), _sliderGain(NULL), _sliderEffectsVolume(NULL), _sliderMusicVolume(NULL) { addButtons(); addSliders(); schedule(schedule_selector(CocosDenshionTest::updateVolumes)); // 提前加载背景音乐与音效,如果内存吃紧的话,就不要提前加载了,如果在win32上开发,进去看源码,会发现此函数为空 // 其他平台下不是这样的,因为引擎主要针对移动平台,在win32上只是一个开发与调试,所以有可能会有不理想的效果,这都 // 无关紧要的。 SimpleAudioEngine::getInstance()->preloadBackgroundMusic( MUSIC_FILE ); SimpleAudioEngine::getInstance()->preloadEffect( EFFECT_FILE ); // 设置默认音量 SimpleAudioEngine::getInstance()->setEffectsVolume(0.5); SimpleAudioEngine::getInstance()->setBackgroundMusicVolume(0.5); } CocosDenshionTest::~CocosDenshionTest() { } void CocosDenshionTest::onExit() { Layer::onExit(); //一定要记得释放掉声音引擎 SimpleAudioEngine::end(); } void CocosDenshionTest::addButtons() { auto lblMusic = LabelTTF::create("Control Music", "Arial", 24); addChildAt(lblMusic, 0.25f, 0.9f); Button *btnPlay = Button::createWithText("play"); //lambda表达式注册回调函数 btnPlay->onTriggered([]() { SimpleAudioEngine::getInstance()->playBackgroundMusic(MUSIC_FILE, true); }); addChildAt(btnPlay, 0.1f, 0.75f); //此处省略N多行代码 ...... Button *btnPlayEffect = Button::createWithText("play"); //由于要用到类成员变量,所以lambda表达式里面带上了"[this]" //详细请看链接http://blog.csdn.net/ac_huang/article/details/27574889 btnPlayEffect->onTriggered([this]() { const float pitch = _sliderPitch->getValue(); const float pan = _sliderPan->getValue(); const float gain = _sliderGain->getValue(); _soundId = SimpleAudioEngine::getInstance()->playEffect(EFFECT_FILE, false, pitch, pan, gain); }); addChildAt(btnPlayEffect, 0.6f, 0.8f); //此处省略N行代码 ...... } //根据百分比来添加控件的位置 void CocosDenshionTest::addChildAt(Node *node, float percentageX, float percentageY) { const Size size = VisibleRect::getVisibleRect().size; node->setPosition(Point(percentageX * size.width, percentageY * size.height)); addChild(node); } //SimpleAudioEngine是我们用来控制音乐、音效的类,他是单个例子 void CocosDenshionTest::updateVolumes(float) { //通过判断当前他们是否有改变来区别更新哪一个 const float musicVolume = _sliderMusicVolume->getValue(); if (fabs(musicVolume - _musicVolume) > 0.001) { _musicVolume = musicVolume; //用来改变背景音乐的声音大小 SimpleAudioEngine::getInstance()->setBackgroundMusicVolume(_musicVolume); } const float effectsVolume = _sliderEffectsVolume->getValue(); if (fabs(effectsVolume - _effectsVolume) > 0.001) { _effectsVolume = effectsVolume; //用来改变背景音效的声音大小 SimpleAudioEngine::getInstance()->setEffectsVolume(_effectsVolume); } }