はじめに
前回の記事ではアイテム使用システムを実装しました。今回はそろそろ耳が寂しくなってきましたので、BGMと効果音(SE)を追加します。AudioEngine を使ったシーンごとのBGM切り替えと、ギミック・アイテム操作に連動した効果音の実装を行います。
- 第1回【Axmol Engine】 スプラッシュ画面シーンを実装する
- 第2回【Axmol Engine】 タイトルシーンを作って画像を表示する
- 第3回【Axmol Engine】 タップした領域への移動
- 第4回【Axmol Engine】 ノードを使用してポップアップを実装する
- 第5回【Axmol Engine】 テクスチャアトラスでダイヤル錠UIを実装する
- 第6回【Axmol Engine】 リファクタリング(メモリ消費対応、ステージをJSON管理、処理修正)+戻るボタン設置
- 第7回【Axmol Engine】 ローカルストレージでステージ進捗管理・ギミックをJSONで管理
- 第8回【Axmol Engine】 アイテムシステムの実装1 - アイテムの取得 -
- 第9回【Axmol Engine】 アイテムシステムの実装2 - アイテムの選択・表示 -
- 第10回【Axmol Engine】 アイテムシステムの実装3 - アイテムの使用 -
- 第11回【Axmol Engine】 BGM・効果音の実装 -(今回)
BGMはGeminiで生成。
効果音は下記サイトから頂戴いたしました。
1. BGM素材の配置とシーンごとの再生
プロンプト:
Content/sounds/以下にタイトル用のBGM=bgm_title.mp3、ゲーム中のBGM=bgm_game.mp3を配置しましたので覚えておいてください
プロンプト:
タイトルシーンではbgm_title.mp3を再生、メインシーンではbgm_game.mp3を再生してください
シーン移動してBGMが変更される時は、停止BGMはメモリからも削除してください
TitleScene と MainScene それぞれの init() でBGMを再生し、シーンを離れる際に stop と uncache でメモリを解放します。
// 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を効果音で鳴らしてください
プロンプト:
タイトルシーンからメインシーンへ遷移するとき、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. ダイヤル錠の効果音(回転・正解・リセット)
プロンプト:
ダイヤル錠を回すたびに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"を効果音として鳴らしてください
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"を追加して、このアトラクションが再生された時に効果音として鳴らしてください
UseItemAttractionDef に effect を追加し、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 では背景の差し替えをスキップし、直前の画像を維持したまま duration と effect のみを適用します。
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_ID は stop() や 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 |




