3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Axmol Engine】 バイブコーディングで脱出ゲームを作る[ 11 ]:BGM・効果音の実装

3
Last updated at Posted at 2026-07-19

はじめに

前回の記事ではアイテム使用システムを実装しました。今回はそろそろ耳が寂しくなってきましたので、BGMと効果音(SE)を追加します。AudioEngine を使ったシーンごとのBGM切り替えと、ギミック・アイテム操作に連動した効果音の実装を行います。



BGMはGeminiで生成。
効果音は下記サイトから頂戴いたしました。

1. BGM素材の配置とシーンごとの再生

プロンプト:
Content/sounds/以下にタイトル用のBGM=bgm_title.mp3、ゲーム中のBGM=bgm_game.mp3を配置しましたので覚えておいてください

スクリーンショット 2026-06-21 10.11.36.png

プロンプト:
タイトルシーンではbgm_title.mp3を再生、メインシーンではbgm_game.mp3を再生してください
シーン移動してBGMが変更される時は、停止BGMはメモリからも削除してください

TitleSceneMainScene それぞれの init() でBGMを再生し、シーンを離れる際に stopuncache でメモリを解放します。

// TitleScene.cpp
static constexpr const char* kBgmTitle = "sounds/bgm_title.mp3";

bool TitleScene::init()
{
    // ...
    _bgmId = AudioEngine::play2d(kBgmTitle, true, 0.7f);
    return true;
}

void TitleScene::goToMain()
{
    if (_transitioning) return;
    _transitioning = true;
    AudioEngine::stop(_bgmId);
    AudioEngine::play2d(kSeTapToStart, false);
    this->runAction(Sequence::create(
        DelayTime::create(2.0f),
        CallFunc::create([this]() {
            AudioEngine::uncache(kBgmTitle);
            auto mainScene = utils::createInstance<MainScene>();
            _director->replaceScene(TransitionFade::create(0.5f, mainScene));
        }),
        nullptr));
}
// MainScene.cpp
static constexpr const char* kBgmGame = "sounds/bgm_game.mp3";

bool MainScene::init()
{
    // ...
    _bgmId = AudioEngine::play2d(kBgmGame, true, 0.7f);
    return true;
}

MainScene::~MainScene()
{
    if (_bgmId != AudioEngine::INVALID_AUDIO_ID)
    {
        AudioEngine::stop(_bgmId);
        AudioEngine::uncache(kBgmGame);
    }
}

2. はまりポイント:実機でBGMが聞こえない

BGMが再生されないです

AudioEngine::play2d() の戻り値(audioID)を確認したところ正常値が返っており、再生自体は成功していました。原因はiPhone本体のサイレントスイッチ(マナーモードスイッチ)でした。

Axmolの AudioEngineImpl は実機で AVAudioSessionCategoryAmbient を使用しており、これはサイレントスイッチに従う仕様のため、スイッチがオンの状態では音が鳴りません。スイッチをオフにすることで解決しました。

サイレントスイッチを無視する AVAudioSessionCategoryPlayback へのカテゴリ変更も検討しましたが、エンジン側のコードは改変しない方針にしました。


3. タイトル→メイン遷移時の効果音

プロンプト:
タイトルシーンからメインシーンへ遷移するときにse_tap_to_start.mp3を効果音で鳴らしてください

スクリーンショット 2026-07-02 13.50.32.png

プロンプト:
タイトルシーンからメインシーンへ遷移するとき、se_tap_to_start.mp3が鳴ってから2秒後に遷移するように

static constexpr const char* kSeTapToStart = "sounds/se_tap_to_start.mp3";

void TitleScene::goToMain()
{
    if (_transitioning) return;
    _transitioning = true;
    AudioEngine::stop(_bgmId);
    AudioEngine::play2d(kSeTapToStart, false);
    this->runAction(Sequence::create(
        DelayTime::create(2.0f),
        CallFunc::create([this]() {
            AudioEngine::uncache(kBgmTitle);
            auto mainScene = utils::createInstance<MainScene>();
            _director->replaceScene(TransitionFade::create(0.5f, mainScene));
        }),
        nullptr));
}

4. ダイヤル錠の効果音(回転・正解・リセット)

スクリーンショット 2026-07-02 13.50.32.png

プロンプト:
ダイヤル錠を回すたびにse_dial.mp3を鳴らしたいので、gimmicks.jsonのdial1に"effect": "sounds/se_dial.mp3"を追加して処理に反映してください

プロンプト:
gimmicks.jsonの"dial1"に"effectSuccess": "sounds/se_success.mp3"を追加して、ギミック正解時に効果音として鳴らしてください

プロンプト:
gimmicks.jsonの"dial1"に"effectReset": "sounds/se_reset.mp3"を追加して、ギミックをリセットする時(btn_reload.pngがタップされた時)に効果音として鳴らしてください

GimmickDef に3フィールドを追加し、JSONで各タイミングの効果音を定義できるようにします。

{
  "dial1": {
    "type": "1",
    "bg": "dial/base.png",
    "effect": "sounds/se_dial.mp3",
    "effectSuccess": "sounds/se_success.mp3",
    "effectReset": "sounds/se_reset.mp3"
  }
}
struct GimmickDef {
    // ...
    std::string effect;
    std::string effectSuccess;
    std::string effectReset;
};

各イベントハンドラ内で再生します。

// ダイヤルを回した瞬間
if (!def.effect.empty())
    AudioEngine::play2d(def.effect, false);

// 正解した瞬間
if (!def.effectSuccess.empty())
    AudioEngine::play2d(def.effectSuccess, false);

// btn_reload.png タップ時
if (!def.effectReset.empty())
    AudioEngine::play2d(def.effectReset, false);

5. アイテム取得時の効果音

アイテムが所持状態になった時に、"se_get_item.mp3"を効果音として鳴らしてください

スクリーンショット 2026-07-02 13.50.32.png

ud->setStringForKey(("item_" + item.itemNo).c_str(), "1");
ud->flush();
AudioEngine::play2d("sounds/se_get_item.mp3", false);

6. アイテム使用演出(attractions)の効果音

useitems.jsonの"attractions"配列オブジェクトの1番目に"effect": "sounds/se_use_key.mp3"を追加して、このアトラクションが再生された時に効果音として鳴らしてください

スクリーンショット 2026-07-02 13.50.32.png

UseItemAttractionDefeffect を追加し、attraction 画像を切り替える CallFunc の中で再生します。

struct UseItemAttractionDef {
    std::string bg;
    float       duration = 1.0f;
    std::string effect;
};
"attractions": [
  {
    "bg": "attractions/attraction_003.png",
    "duration": 3.0,
    "effect": "sounds/se_use_key.mp3"
  }
]
actions.pushBack(CallFunc::create([overlay, bg, effect, visibleSize]() {
    // 背景差し替え処理...
    if (!effect.empty())
        AudioEngine::play2d(effect, false);
}));

7. attractionsで "bg" を省略した場合の対応

useitems.jsonの"attractions"配列オブジェクトの2番目に"bg"無しの定義をしましたので、この場合は背景画像は直前のものを表示しておくようにしてください

"bg" が省略された attraction では背景の差し替えをスキップし、直前の画像を維持したまま durationeffect のみを適用します。

actions.pushBack(CallFunc::create([overlay, bg, effect, visibleSize]() {
    if (!bg.empty())
    {
        overlay->removeChildByTag(251);
        auto bgSp = Sprite::create(bg);
        if (bgSp)
        {
            bgSp->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
            bgSp->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2 + 50));
            bgSp->setTag(251);
            overlay->addChild(bgSp, 0);
        }
    }
    if (!effect.empty())
        AudioEngine::play2d(effect, false);
}));

"bg" を省略したJSONを読み込んだ際に Assertion failed: (false), function operator[], file document.h が発生しました。loadUseItemDefs() 内で a["bg"]HasMember チェックなしに直接アクセスしていたことが原因です。他のフィールドと同様に HasMember チェックを追加して解消しました。

def.attractions.push_back({
    .bg       = a.HasMember("bg")       ? a["bg"].GetString()      : "",
    .duration = a.HasMember("duration") ? a["duration"].GetFloat() : 1.0f,
    .effect   = a.HasMember("effect")   ? a["effect"].GetString()  : "",
});

AudioEngine::play2d について

BGM・SEどちらも同じ関数で扱います。

static AUDIO_ID play2d(std::string_view filePath,
                       bool loop = false,
                       float volume = 1.0f,
                       float time = 0.0f);
引数 意味
filePath Content からの相対パス
loop ループ再生するか(BGM: true / SE: false)
volume 0.0〜1.0の音量
time 再生開始位置(秒)

戻り値の AUDIO_IDstop()setVolume() などで個別の再生インスタンスを制御する際に使います。失敗時は AudioEngine::INVALID_AUDIO_ID が返ります。BGMのようにループ・停止・音量制御が必要なものはIDを保持し、SEのように鳴らしっぱなしでよいものは戻り値を捨てて呼び出すだけで済みます。


成果物

BGMと効果音で臨場感が出てきたのではと思います!


まとめ

実装内容 対応ファイル
BGMのシーン別再生・メモリ解放 TitleScene.cpp / MainScene.cpp
タップ音・ダイヤル各種SE gimmicks.json + MainScene.cpp
アイテム取得音 MainScene.cpp
アイテム使用演出音・背景省略対応 useitems.json + MainScene.cpp
音量調整 BGM 0.7 / SE 1.0
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?