Cocos2d-x 3.x で音楽をフェードアウト・フェードインさせる

Cocos2d-x 標準の SimpleAudioEngine はあまりにもシンプルすぎて音楽のフェードにすら対応していない。これを解決しようという試みが Music Fader for Cocos2dx でなされているが、このコードはバージョンが古く、おそらく 3.x 系で追加されたのであろう pure virtual な関数が実装されていないため、このままでは使用できない。この点を改善したコードを書いたので載せる。自由に使っていただいて構わない。

使い方

Node に対する Action として用いる。たとえば Scene に直接つけちゃおう(適当)。

nodeInstance->runAction(MusicFade::create(1.0f, 0.0f, true));

MusicFade.h

// This code is originally from http://www.fuguelike.com/music-fader-for-cocos2dx/
// but altered to make it work with Cocos2d-x 3.x

#ifndef __MusicFade__
#define __MusicFade__

#include "cocos2d.h"
#include "nmd.h"

class MusicFade : public cocos2d::ActionInterval
{
public:
    MusicFade();

    static MusicFade* create(float d, float volume, bool pauseOnComplete = false );
    bool initWithDuration(float d, float volume, bool pauseOnComplete );

    virtual MusicFade* clone() const override; // pure virtual
    virtual MusicFade* reverse() const override; // pure virtual
    virtual void startWithTarget(cocos2d::Node *pTarget);
    virtual void update(float time);
    virtual void stop(void);

protected:
    float m_targetVal;
    float m_initialVal;
    bool m_bPauseOnComplete;
};

#endif /* defined(__MusicFade__) */

MusicFade.cpp

#include "MusicFade.h"
#include "SimpleAudioEngine.h"

using namespace cocos2d;
using namespace CocosDenshion;

MusicFade::MusicFade()
{
    m_initialVal = 0;
    m_targetVal = 0;
}

MusicFade* MusicFade::create(float duration, float volume, bool pauseOnComplete)
{
    MusicFade *pAction = new MusicFade();
    pAction->initWithDuration(duration, volume, pauseOnComplete);
    pAction->autorelease();

    return pAction;
}

bool MusicFade::initWithDuration(float duration, float volume, bool pauseOnComplete)
{
    if (CCActionInterval::initWithDuration(duration))
    {
        m_targetVal = volume;
        m_bPauseOnComplete = pauseOnComplete;
        return true;
    }

    return false;
}

void MusicFade::update(float time)
{
    float vol = m_initialVal + time*(m_targetVal - m_initialVal);
    SimpleAudioEngine::getInstance()->setBackgroundMusicVolume(vol);
}

MusicFade* MusicFade::clone(void) const
{
    // no copy constructor
    auto a = new MusicFade();
    a->initWithDuration(_duration, m_targetVal, m_bPauseOnComplete);
    a->autorelease();
    return a;
}

MusicFade* MusicFade::reverse() const
{
    return MusicFade::create(_duration, m_targetVal);
}

void MusicFade::startWithTarget(Node *pTarget)
{
    CCActionInterval::startWithTarget( pTarget );
    m_initialVal = SimpleAudioEngine::getInstance()->getBackgroundMusicVolume();
}

void MusicFade::stop(void)
{
    if(m_bPauseOnComplete) SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
    CCActionInterval::stop();
}