ファイルをプラグイン外に置く時
| 用途 | 推奨形式 | 理由 |
|---|---|---|
| BGM | MP3 (+ OGG を fallback) | 圧縮率が高く、ループ再生に向いている |
| SE | WebM/OGG (+ MP3 を fallback) | 遅延が少なく、短音の再生に最適 |
BGM には MP3 が最適
- 全ブラウザ対応で安心
- ファイルサイズを小さく抑えられる(サーバー負荷・読み込み速度)
- ループ再生(
audio.loop = true)も問題なく動作 - ビットレートは 128kbps 程度で十分(192kbpsでも可)
SE には OGG / WebM が最適
- MP3より再生遅延(レイテンシ)が少ない
- ゲームのSEは「タイミングが命」なので遅延は致命的
- ただしSafariがOGGを非対応なので MP3をfallbackに用意するのがベスト
実装例(Web Audio API推奨)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// BGM const bgm = new Audio(); bgm.src = 'bgm.mp3'; bgm.loop = true; bgm.volume = 0.5; bgm.play(); // SE(Web Audio API で低遅延再生) const audioCtx = new AudioContext(); async function loadSE(url) { const res = await fetch(url); const buf = await res.arrayBuffer(); return await audioCtx.decodeAudioData(buf); } function playSE(buffer) { const source = audioCtx.createBufferSource(); source.buffer = buffer; source.connect(audioCtx.destination); source.start(0); // 遅延なし! } const jumpSE = await loadSE('jump.ogg'); playSE(jumpSE); // 呼び出し |
WordPress特有の注意点
MIMEタイプの許可
WordPressはデフォルトでOGG/WebMをメディアライブラリにアップロードできない場合があります。functions.php に追加:
|
1 2 3 4 5 6 7 |
// Cocoonの場合、子テーマのfunctions.phpに追記 add_filter('upload_mimes', function($mimes) { $mimes['ogg'] = 'audio/ogg'; $mimes['webm'] = 'audio/webm'; $mimes['mp3'] = 'audio/mpeg'; return $mimes; }); |
ファイルの置き場所
メディアライブラリより、テーマフォルダや専用ディレクトリに置く方が管理しやすいです:
|
1 2 3 4 |
/wp-content/themes/cocoon-child/assets/audio/ ├── bgm.mp3 ├── jump.ogg └── jump.mp3 ← Safariのfallback用 |
まとめ
|
1 2 3 |
BGM → MP3 一本で OK SE → OGG + MP3 (fallback) の2セット用意 再生 → SEはWeb Audio APIを使うと遅延ゼロで快適 |
プラグインフォルダからMP3を呼び出す方法
フォルダ構成(推奨)
|
1 2 3 4 5 6 7 |
/wp-content/plugins/your-plugin/ ├── your-plugin.php └── assets/ └── audio/ ├── bgm.mp3 ├── jump.ogg └── jump.mp3 |
PHPでURLを取得する
plugins_url() を使うのがWordPress公式の正しいやり方です。
|
1 2 3 |
// プラグインフォルダ内のファイルURLを取得 $audio_url = plugins_url('assets/audio/bgm.mp3', __FILE__); // 結果例: https://example.com/wp-content/plugins/your-plugin/assets/audio/bgm.mp3 |
ショートコードへの組み込み例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
function my_game_shortcode() { // 音声ファイルのURLをPHPで生成 $bgm_url = plugins_url('assets/audio/bgm.mp3', __FILE__); $jump_url = plugins_url('assets/audio/jump.ogg', __FILE__); $jump_mp3 = plugins_url('assets/audio/jump.mp3', __FILE__); ob_start(); ?> <canvas id="gameCanvas"></canvas> <script> // PHPで生成したURLをJavaScriptに渡す const AUDIO = { bgm: "<?php echo esc_url($bgm_url); ?>", jump: "<?php echo esc_url($jump_url); ?>", jumpMp3: "<?php echo esc_url($jump_mp3); ?>" }; // BGM const bgm = new Audio(AUDIO.bgm); bgm.loop = true; // SE(Web Audio API) const audioCtx = new AudioContext(); async function loadSE(url) { const res = await fetch(url); const buf = await res.arrayBuffer(); return await audioCtx.decodeAudioData(buf); } </script> <?php return ob_get_clean(); } add_shortcode('my_game', 'my_game_shortcode'); |
MIMEタイプの許可も忘れずに
OGGを使う場合、プラグインの your-plugin.php に追記:
|
1 2 3 4 |
add_filter('upload_mimes', function($mimes) { $mimes['ogg'] = 'audio/ogg'; return $mimes; }); |
ポイントまとめ
| やること | 関数・方法 |
|---|---|
| プラグイン内ファイルのURL取得 | plugins_url('パス', __FILE__) |
| JavaScriptへのURL受け渡し | PHPをscriptタグ内に直接埋め込む |
| OGGのアップロード許可 | upload_mimes フィルター |
Web Audio APIで事前読み込みする方法
new Audio() を毎回作るのではなく、ゲーム開始前に一括でバッファに読み込んでおくやり方です。
基本的な仕組み
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// 1. AudioContextを作成 const audioCtx = new AudioContext(); // 2. 起動時に全SE読み込み(バッファに保持) const seBuffers = {}; async function loadAllSE() { const seList = { hit1: myAppVars.SeHit1, hit2: myAppVars.SeHit2, hit3: myAppVars.SeHit3, }; for (const [key, url] of Object.entries(seList)) { const res = await fetch(url); const arrayBuffer = await res.arrayBuffer(); seBuffers[key] = await audioCtx.decodeAudioData(arrayBuffer); // ↑ デコード済みのデータをメモリに保持しておく } } // 3. 再生時はバッファから即座に鳴らす function playSE(key, volume = 1.0) { const buffer = seBuffers[key]; if (!buffer) return; const source = audioCtx.createBufferSource(); const gainNode = audioCtx.createGain(); source.buffer = buffer; gainNode.gain.value = volume; // source → gain → スピーカー source.connect(gainNode); gainNode.connect(audioCtx.destination); source.start(0); // 遅延なし! } |
ゲーム開始時の使い方
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
async function startGame() { // AudioContextはユーザー操作後に作る(ブラウザ制限対策) if (!audioCtx) { audioCtx = new AudioContext(); } await loadAllSE(); // 読み込みが終わってからゲーム開始 // ゲーム本体の処理... } // 使うとき playSE("hit1"); // 普通の音量 playSE("hit2", 0.5); // 小さめ |
new Audio() との比較
new Audio() | Web Audio API(バッファ) | |
|---|---|---|
| 読み込みタイミング | 毎回 | 起動時に1回だけ |
| 再生の遅延 | あり(数十〜数百ms) | ほぼゼロ |
| 連打・重複再生 | 不安定 | 安定して対応できる |
| 音量・エフェクト制御 | 限定的 | 細かく制御可能 |
| コード量 | 少ない | やや多い |
注意点
AudioContextはユーザー操作後に作ることが重要です。ページ読み込み直後に new AudioContext() を作るとブラウザに止められる場合があります。「ゲームスタートボタンを押した時」に作るのがベストです。
AudioContextは有限
audioCtx と seBuffers のリセットについて
audioCtx は null にしない方が良いです
AudioContext はブラウザがユーザー操作ごとに作れる数に制限があります(概ね6〜8個)。nullにして毎回 new AudioContext() を作り直すと、ゲームを繰り返すうちに上限に達して警告や不具合が出ることがあります。
推奨:一度作ったら使い回す
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// モジュールスコープで1つだけ持つ let audioCtx = null; let seBuffers = {}; async function startGame() { // なければ作る、あれば使い回す if (!audioCtx) { audioCtx = new AudioContext(); } // suspendされていたら再開 if (audioCtx.state === "suspended") { await audioCtx.resume(); } await loadAllSE(); // ... } |
seBuffers はゲームをまたいで同じSEを使うなら不要です
同じSEを次のゲームでも使うなら、nullにせず持ち続けた方が効率的です。
|
1 2 3 4 5 6 7 8 9 10 |
// ゲーム終了時 function endGame() { // seBuffers はそのまま → 次回loadAllSE()が不要になる // audioCtx もそのまま使い回す if (runtime.bgm) { runtime.bgm.pause(); runtime.bgm = null; } } |
| 推奨 | 理由 | |
|---|---|---|
audioCtx | 使い回す | 作れる数に上限がある |
seBuffers | 使い回す | 毎回fetchが不要になり速い |
BGMのAudio()オブジェクト | 毎回null化してOK | 軽いので問題なし |
SEとaudioCtxはゲームをまたいで持ち続け、BGMだけ都度作り直すのが一番シンプルです!
ページリロード時のaudioCtxについて
結論:リロードで自動的に破棄されます
ページがリロードされるとJavaScriptの実行環境ごとリセットされるので、audioCtx を含む全ての変数は消えます。
リロード時に起きること
|
1 2 3 4 5 6 7 8 9 10 11 |
ページリロード ↓ JSの実行環境が完全にリセット ↓ audioCtx → 消える(ブラウザが自動でcloseして解放) seBuffers → 消える その他の変数 → 全部消える ↓ JSが最初から読み込み・実行される ↓ audioCtx = null の初期状態からスタート |
ブラウザが持つAudioContextの個数制限もリロードでリセットされるので、何も心配しなくて大丈夫です。
つまりこういうことです
| タイミング | audioCtx |
|---|---|
| 同一ページ内でゲームを繰り返す | 使い回す(nullにしない) |
| ページリロード | 自動で破棄・解放される |
コメント