VANNAN GAMES.

← 制作ノート一覧

パチンコ風カットインの効果音を3回作り直した話(iOSマナーモードの罠)

フルスタック!

『フルスタック!』では、強敵が出現するとパチンコの当たり演出みたいなカットインが入ります。画面が暗転し、帯が走り、ドン!と止まる。ここに「キュイーン!」という音が欲しい。当然そう思って作ったのですが、この効果音、「聞こえない」という報告を3回もらって3回作り直しました。その記録です。

1回目: 単発ビープは演出に負ける

このサイトのゲームは効果音をWeb Audio APIで合成しています(音源ファイルなしで鳴らせるので読み込みが速い)。初版のカットインSEも他のSEと同じノリで、オシレータ1本のビープ音でした。結果は「SEがないですね」。鳴ってはいたのです。ただ音量0.2の単発ビープは、BGMと派手な画面演出に完全に埋もれていました。視覚のド派手さに音が釣り合わないと、人間の耳には「無音」として処理されるようです。

2回目: 多層シンセ+BGMダッキング

そこで音を3層に作り直しました。ノイズをバンドパスフィルタで掃引する風切りのライザー、低いサイン波のドン、そして倍音3層を480Hz→2600Hzへ滑らせてビブラートを掛けた「キュイーン」。さらにカットイン中だけBGMの音量を0.18→0.02まで絞るダッキングも入れました。PCのブラウザでは完璧でした。しかし実機のiPhoneでは、まだ「聞こえない」。

犯人はマナーモードだった

調べて分かったのは、iOSの消音スイッチ(マナーモード)の挙動でした。HTMLのAudio要素で再生する音(BGM)は鳴るのに、Web Audio APIで合成した音(SE)だけが消音されるのです。つまりマナーモードのiPhoneでは「BGMは聞こえるのにSEが一切鳴らない」という、まさに報告どおりの状態になります。navigator.audioSession.type = "playback" を宣言すると回避できる端末もありますが、対応はSafari 16.4以降で、確実ではありません。

3回目: 確実に鳴る経路に乗せる

最終判断はシンプルでした。BGMが鳴る経路なら、SEも鳴る。つまりAudio要素+音声ファイルにすればいい。Web Audioで設計した波形をPythonで合成し直してWAVファイルに書き出し(風切り→0.32秒でドン→キュイーン、ボス級は2連キュインの激アツ仕様)、カットインの瞬間にAudio要素で再生する方式に変えました。ドンの位置を0.32秒に固定してあるのは、画面が揺れる演出のタイミングと波形レベルで同期させるためです。

合成スクリプトはリポジトリに残してあるので、音色の調整はコードをいじって再生成するだけです。「ファイルを使わない」というこだわりは捨てましたが、こだわりはユーザーに聞こえてこそです。

WAVをコードから合成する

最終版の音声ファイルは、DAWではなくPythonスクリプトで合成しています。ノイズをバンドパスフィルタに通しながら中心周波数を300Hz→4200Hzへ掃引すると風切りのライザーになり、サイン波を70Hz→28Hzへ落とすと「ドン」になります。主役のキュイーンは、矩形波・倍音のノコギリ波・1オクターブ下のサイン波の3層を480Hz→2600Hzへ滑らせ、24Hzの細かいビブラートを掛けたもの。仕上げに90ミリ秒のエコーを足し、全体をソフトクリップで軽く歪ませて音圧を稼いでいます。パチンコの当たり音の「あの感じ」は、上昇グリスとビブラートの2要素でだいたい再現できると分かったのが収穫でした。

スクリプトで作る利点は再現性です。「もう少し高く」「2連にしたい」が数値の変更と再実行で済み、生成物をリポジトリに置かなくてもスクリプトさえあれば同じ音を作り直せます。効果音のためにDAWを立ち上げる腰の重さがない、というのは個人開発では案外大きいのです。

音は「引き算」も設計する

鳴らす工夫と同じくらい効いたのが、BGMを引っ込める工夫でした。カットインの1.5秒間だけBGMの音量を通常の0.18から0.02まで絞り、SEが終わる頃に戻します。映画の爆発シーンで音楽が消えるのと同じ理屈で、無音に近い地の上でこそ効果音は大きく聞こえます。SEが埋もれると感じたら、SEを大きくする前にBGMを下げられないか考える——ミックスの初歩ですが、ゲームだと忘れがちです。

教訓

効果音の不具合報告は「鳴っていない」ではなく「聞こえない」と読むべきでした。1回目は音量とミックスの問題、2回目は再生経路の問題で、コードはどちらも「正しく」動いていました。動作確認をPCのChromeだけで済ませていたら、この2つは一生見つからなかったと思います。