記事の目的
前回、一時停止機能の実装を行いました。
前回記事 ⇒ #11_一時停止機能の実装
AudioMixer設定
ChatGPTの回答(クリックで展開)
Unityの Audio Mixer は「音量・音質・ルーティング(流れ)」をプロジェクト全体で一元管理できるミキサー卓です。BGM/SE/UI音などをまとめて調整したり、ポーズ時にローパスをかけたり、SEが鳴った瞬間にBGMを自動で下げる(ダッキング)などができます。以下、実務でそのまま使える手順で解説します。
基本の考え方(3要素)
- AudioMixer:ミキサー資産(
.mixer
)。中に複数のGroup(バス)を作る。 - AudioMixer Group:音の通り道。各Groupにエフェクトや音量を設定。
- Snapshot:ミキサーの状態を丸ごと保存したプリセット(後でスクリプトで切替可能)。
1) 準備:MixerとGroupを作る
- Assets > Create > Audio Mixer でミキサーを作成(例:
MasterMixer
)。 - Window > Audio > Audio Mixer を開く。
- 左のツリーで
Master
を右クリック → Create Child Group。
例)Music
/SFX
/UI
。必要ならSFX/Hit
、SFX/Expl
のように下位グループも。 - ルーティングは既定で親(最終的に
Master
)へ流れます。特に弄らなければOK。
2) AudioSourceをGroupへ流す(ルーティング)
各AudioSource(BGMやSEを鳴らすオブジェクト)の Output に、対応する AudioMixerGroup を割り当てます。
例)BGM用AudioSource → Music
、効果音用AudioSource → SFX
。
スクリプトで割り当てたい場合:
using UnityEngine;
using UnityEngine.Audio;
public class RouteExample : MonoBehaviour
{
[SerializeField] private AudioSource src;
[SerializeField] private AudioMixerGroup sfxGroup;
void Awake()
{
src.outputAudioMixerGroup = sfxGroup; // SFXへルーティング
}
}
AudioMixer.FindMatchingGroups("SFX")[0]
で検索も可能ですが、Inspector 参照のほうが安全です。
3) 音量スライダー連動(Exposed Parameter)
狙い: UIスライダーで Master / Music / SFX の音量を個別に調整。
- Audio Mixerウィンドウで、各Groupの Attenuation(Volume) つまみを右クリック → Expose 'Volume (of Music)' to script
- 右上の Exposed Parameters リストで分かりやすい名前に変更
例)MasterVolume
/MusicVolume
/SFXVolume
using UnityEngine;
using UnityEngine.Audio;
public class VolumeController : MonoBehaviour
{
[SerializeField] private AudioMixer mixer;
public void SetMasterVolume(float linear) => SetVolume("MasterVolume", linear);
public void SetMusicVolume (float linear) => SetVolume("MusicVolume", linear);
public void SetSFXVolume (float linear) => SetVolume("SFXVolume", linear);
private void SetVolume(string exposedName, float linear)
{
// 0を避ける(logで-∞になるため)
float v = Mathf.Clamp(linear, 0.0001f, 1f);
float dB = Mathf.Log10(v) * 20f; // 1→0dB, 0.5→約-6dB
mixer.SetFloat(exposedName, dB);
}
}
UIの Slider.onValueChanged
に上記メソッドを割り当てれば完成。
保存は PlayerPrefs
でスライダー値を保存・復元すると便利です。
4) Snapshotで“シーン/状態”を一括切替
用途例: Paused(全体-10dB&ローパス)/ Underwater(強ローパス+少しリバーブ)/ Battle(SFX上げ・Music下げ)
- Audio Mixer右上の Snapshots から +(例:
Default
,Paused
)。 - スナップショットごとに各Groupのボリュームやエフェクトを調整して保存。
using UnityEngine;
using UnityEngine.Audio;
public class SnapshotSwitcher : MonoBehaviour
{
[SerializeField] private AudioMixerSnapshot defaultShot;
[SerializeField] private AudioMixerSnapshot pausedShot;
public void ToDefault(float time = 0.5f) => defaultShot.TransitionTo(time);
public void ToPaused (float time = 0.5f) => pausedShot.TransitionTo(time);
}
複数Snapshotを重みでブレンド:
mixer.TransitionToSnapshots(new[]{ shotA, shotB }, new[]{ 0.3f, 0.7f }, 1.0f);
5) ダッキング(SEが鳴ったらBGMを自動で下げる)
構成: SFXグループから“送る” → Musicグループで“受けて下げる”
- SFX グループに Send エフェクトを追加。Send Level を上げ、Key 名を設定(例:
DuckKey
)。 - Music グループに Duck Volume エフェクトを追加。Trigger(Sidechain) に同じ Key(
DuckKey
)を指定。 - Threshold / Ratio / Attack / Release を調整。
目安:瞬発的なSEならAttack短め(5–20ms)、自然に戻すならRelease長め(200–400ms)。
効かない時は:Key名一致/Send Level十分か/Music側DuckのTrigger設定を再確認。
6) よく使うエフェクトと使い所
- Lowpass / Highpass: ポーズや水中、無線風。
- Reverb: UIには基本不要。環境音や一部SEに少量。
- Chorus / Distortion / Echo: SFXの彩り。入れすぎ注意。
- Pitch Shifter: 演出用。BGM全体には非推奨。
ポイント:重いエフェクトを各AudioSourceに個別挿しするより、Groupにまとめて挿すほうが軽く管理もしやすい。
7) 2D/3Dの注意
- 2DゲームのBGM/SEは
AudioSource.Spatial Blend = 0 (2D)
推奨。 - 空間演出が必要なSEだけ3D寄りに(足音、爆発など)。
8) トラブルシュート(“音が出ない/小さい/変”)
- シーンに AudioListener はある?(通常は Main Camera に1つ)
- AudioSourceの mute や Group の Mute/Solo が入っていない?
- Exposed名のタイプミス:
SetFloat
の名前は合ってる?GetFloat
で確認可。 - Output未設定:
AudioSource.outputAudioMixerGroup
が None だとMixerの効果は乗らない。 - クリップのImport Settings(Load Type/Compression)で音質が劣化していない?
- Snapshot切替で意図せず
-80 dB
になっていない?
今回からChatGPTのバージョンがGPT-5になったので少し雰囲気が変わりました。
要約していきましょう。
①AudioMixerを設定
②AudioMixer内でGroupを作成
③AudioSourceがアタッチされたオブジェクトにGroupを対応させる
④各GroupのExposedParameterを作成
⑤Snapshotで場面ごとの音量を保存する
⑥音の干渉を防ぐ?ダッキングを設定
①AudioMixerを設定
Window > Audio > Audio Mixer をクリック
ProjectビューのところにAudioMixerが表示される。
②AudioMixer内でGroupを作成
Groups内に存在しているMasterを右クリックし、AddChildGroupを選択。Music、SFX、UIの3グループとさらにその下位グループを作成。
③AudioSourceがアタッチされたオブジェクトにGroupを対応させる
それぞれ、BGMPlayer、SFXPlayer、UIPlayerとしてAudio>AudioSourceから作成。
これにAudioRouter.csをアタッチ
これによりAudioSource(例えばBGM)とAudioMixer内のグループが繋がります。
グループは音量の値を持っているので対象のAudioSourceの音量調節が可能になります。
④各GroupのExposedParameterを作成
Groupを選択してInspectorに表示
Inspector>Attenuation にあるVolumeの文字を右クリック
「Exposed 'Parameter(of 〇〇)' to script」 を選択
⑤Snapshotで場面ごとの音量を保存する
Snapshots新規作成し、場面に応じた音量設定を作成する。
通常ゲーム画面⇒Default
ポーズ画面⇒Paused
AudioMixer画面右上のExposed Parameterをクリック
それぞれに分かりやすい名前を付ける
⑥ダッキングを設定
例としてSFX(効果音)がなっているときにBGMの音量を下げる
BGMのグループを選択 > Inspector > Add effect > Duck Volume
SFXのグループを選択 > Inspector > Add effect > Send
Receiveのプルダウンから送り先(SFXがなった時に音量を変化させたいグループ)を選択
とりあえずの初期設定はここまでとします。
音量設定画面の作成
続いてオプションから音量を変更できるようにしていきます。
ChatGPTの回答で言うと3)音量スライダー連動のあたりです。
最終的なイメージはこんな感じ
①Sliderオブジェクトの作成
音量設定のボタンを押した時に開く"Panel_Volume"に各グループに対応したSliderオブジェクトを作成(UI > Slider)
②Groupを紐づけるためのスクリプトを設定
ChatGPTの回答(クリックで展開)
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Audio;
public class VolumeSlider : MonoBehaviour
{
[SerializeField] private Slider slider;
[SerializeField] private AudioMixer audioMixer;
[SerializeField] private string parameterName; // "Master", "Music", "SFX", "UI" など
[SerializeField] private Text label; // UI Text (スライダーの横に配置)
private void Start()
{
// 初期値を反映
float currentValue;
audioMixer.GetFloat(parameterName, out currentValue);
slider.value = Mathf.Pow(10f, currentValue / 20f); // dB→0-1に変換
UpdateLabel(slider.value);
// イベント登録
slider.onValueChanged.AddListener(OnValueChanged);
}
private void OnValueChanged(float value)
{
// 0-1 を dB に変換 (-80dB ~ 0dB想定)
float dB = Mathf.Log10(Mathf.Clamp(value, 0.0001f, 1f)) * 20f;
audioMixer.SetFloat(parameterName, dB);
// UI 更新(%表示のみ)
UpdateLabel(value);
}
private void UpdateLabel(float value)
{
int pct = Mathf.RoundToInt(value * 100f);
label.text = $"{pct}%"; // ←ここをシンプルに%だけに変更
}
}
↑のスクリプトを各Sliderオブジェクトにアタッチして以下の設定
- Slider : アタッチしたオブジェクトを配置
- Audio Mixer : Audio Mixerのグループから各Groupのミキサーを配置
- Parameter Name : 各Exposed parameterで設定した名前をそのまま入力
- Label : 各SliderごとにTextMeshProでオブジェクトを作成して配置
VolumeSliderの機能は
・各SliderをdB値に変換
・各Sliderのラベルを%表記で表示
スクリプトで数字が変化するためすべて「100%」と入力しています。
加えて"left_〇〇"としているのはそれぞれの「全体:」のように%の前に付ける文字列です。
これらのRect Transformで位置を整えます。
③スライダー操作で音量を変化させる機能
ChatGPTの回答(クリックで展開)
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;
public class VolumeController : MonoBehaviour
{
[Header("Mixer")]
[SerializeField] private AudioMixer mixer; // MasterMixer を割り当て
// MixerのExposed名(Exposed Parametersと絶対一致)
private const string MASTER = "MasterVolume";
private const string MUSIC = "MusicVolume";
private const string SFX = "SFXVolume";
private const string UI_VOL = "UIVolume";
[Header("UI Sliders (0..1)")]
[SerializeField] private Slider sliderMaster; // Slider_Master
[SerializeField] private Slider sliderMusic; // Slider_Music
[SerializeField] private Slider sliderSFX; // Slider_SFX
[SerializeField] private Slider sliderUI; // Slider_UI(使わなければ空でOK)
// 保存キー
private const string KEY_MASTER = "MasterVolume";
private const string KEY_MUSIC = "MusicVolume";
private const string KEY_SFX = "SFXVolume";
private const string KEY_UI = "UIVolume";
private void Awake()
{
// 1) 保存から読み込み(既定1.0)
float vMaster = PlayerPrefs.GetFloat(KEY_MASTER, 1f);
float vMusic = PlayerPrefs.GetFloat(KEY_MUSIC, 1f);
float vSFX = PlayerPrefs.GetFloat(KEY_SFX, 1f);
float vUI = PlayerPrefs.GetFloat(KEY_UI, 1f);
// 2) Mixerに反映
Apply(MASTER, vMaster);
Apply(MUSIC, vMusic);
Apply(SFX, vSFX);
Apply(UI_VOL, vUI);
// 3) スライダーを同期(通知なし)
if (sliderMaster) sliderMaster.SetValueWithoutNotify(vMaster);
if (sliderMusic) sliderMusic .SetValueWithoutNotify(vMusic);
if (sliderSFX) sliderSFX .SetValueWithoutNotify(vSFX);
if (sliderUI) sliderUI .SetValueWithoutNotify(vUI);
// 4) スライダーのイベント登録(0..1の値をdBに変換して保存)
if (sliderMaster) sliderMaster.onValueChanged.AddListener(v => { Apply(MASTER, v); PlayerPrefs.SetFloat(KEY_MASTER, v); });
if (sliderMusic) sliderMusic .onValueChanged.AddListener(v => { Apply(MUSIC, v); PlayerPrefs.SetFloat(KEY_MUSIC, v); });
if (sliderSFX) sliderSFX .onValueChanged.AddListener(v => { Apply(SFX, v); PlayerPrefs.SetFloat(KEY_SFX, v); });
if (sliderUI) sliderUI .onValueChanged.AddListener(v => { Apply(UI_VOL, v); PlayerPrefs.SetFloat(KEY_UI, v); });
}
// 0..1 → dB(1→0dB, 0.5→-6dB, 0.1→-20dB)
private void Apply(string exposedName, float value01)
{
float v = Mathf.Clamp(value01, 0.0001f, 1f); // 0は-∞になるので回避
float dB = Mathf.Log10(v) * 20f;
if (!mixer.SetFloat(exposedName, dB))
Debug.LogWarning($"Exposed parameter not found: {exposedName}");
}
// 逆変換が必要になったら
public static float DbToLinear(float dB) => Mathf.Pow(10f, dB / 20f);
}
あなたの階層での結線(そのまま手順)
AudioManager に VolumeController をアタッチ
mixer に MasterMixer をドラッグ
Slider をドラッグで結ぶ(Min=0 / Max=1 のままでOK)
sliderMaster ← Slider_Master
sliderMusic ← Slider_Music
sliderSFX ← Slider_SFX
sliderUI ← Slider_UI(使うときだけ)
※このスクリプトは自動でリスナー登録するので、各Sliderの On Value Changed は空でOK(既に結んでいるなら二重呼び出しにならないよう片方に統一)。
Mixer 側の「パラメータ名」を必ず一致
Exposed Parameters の名前を MasterVolume / MusicVolume / SFXVolume / UIVolume にする(この順でExpose→右上でリネーム)。
もしMixerの名前が違うなら、スクリプト冒頭の MASTER/MUSIC/SFX/UI_VOL の文字列をMixer側の名前に合わせて変更。
クイック自己診断
コンソールに「Exposed parameter not found: XXX」が出た → Mixer右上の名前が一致してない
スライダーを動かしても変化なし → 該当AudioSourceの Output が正しい Group(Music/SFX/UI)になっていない
補足(スクショの並びについて)
Panel_Volume 直下の Background / Fill Area / Handle Slide Area は、本来は各Sliderの子にある要素です。もし親子関係が崩れていたら、各 Slider_* の子に戻すか、Sliderを作り直すと見た目の崩れや挙動不良を避けられます。
これをAudio内にAudioManager(空のオブジェクト)を作りアタッチ
- Slider 〇〇 : 各Sliderのオブジェクト
- Label 〇〇 : 各Labelのオブジェクト
VolumeControllerの機能は
・各Sliderの値を0~1のdB値に変換してAudioMixerに渡す
・PlayerPrefsに値を保管(再起動しても同じ音量を保持)
・Sliderの変換がある度にMixer/ラベル表記/PlayerPrefsを更新
④Snapshotを設定
Audio内にAudioSnapshotManager(空のオブジェクト)を作成
そこにSnapshotSwitcherをアタッチ
Default,PausedそれぞれにAudioMixer上で作成したSnapshotを配置
⑤オプション画面から実際に音量設定画面に遷移できるようにする
ChatGPTの回答(クリックで展開)
結論:専用スクリプトが無ければ新規作成します。
PauseManager=パネル(PausePanel↔OptionPanel)の開閉のみ、新規スクリプト=OptionPanel内ページ切替のみと役割分離して競合を避けます。
目的
- OptionPanel を開いたあと、「音量設定(Panel_Volume)」ページへの遷移/戻るを行う。
- PauseManager と重複しない(OptionPanel の内側だけを切替)。
① 推奨ヒエラルキー(最小変更)
PauseCanvas
├─ PausePanel
│ ├─ ResumeButton
│ ├─ RestartButton
│ ├─ RetireButton
│ └─ OptionButton ← OnClick: PauseManager.OpenOptions()
├─ ConfirmPanel
└─ OptionPanel ← PauseManager から開く
├─ Page_OptionsHome ← ★新規の空オブジェクト(下の4つを中に入れる)
│ ├─ OptionTitle
│ ├─ SoundOption (Button) ← 「音量設定へ」
│ ├─ DisplayOption
│ └─ KeyOption
├─ Panel_Volume ← 既存(スライダー群)※初期は非表示
└─ BackButton (Button) ← 「オプションHOMEへ戻る」
初期状態:Page_OptionsHome = Active、Panel_Volume = Inactive。
② スクリプト(OptionPanel 内のみを切替)
OptionsNavigator.cs(新規。PauseManagerとは別ファイル)
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class OptionsNavigator : MonoBehaviour
{
[Header("Container under OptionPanel")]
[SerializeField] private GameObject pageOptionsHome; // PauseCanvas/OptionPanel/Page_OptionsHome
[SerializeField] private GameObject pageVolume; // PauseCanvas/OptionPanel/Panel_Volume
[Header("First Select (optional)")]
[SerializeField] private Selectable firstOnHome; // 例: SoundOption の Button
[SerializeField] private Selectable firstOnVolume; // 例: Panel_Volume 内の Slider_Master
void Awake()
{
// OptionPanel を開いた時点の想定初期表示
ShowHome();
}
public void ShowHome()
{
if (pageOptionsHome) pageOptionsHome.SetActive(true);
if (pageVolume) pageVolume.SetActive(false);
if (firstOnHome) EventSystem.current?.SetSelectedGameObject(firstOnHome.gameObject);
}
public void ShowVolume()
{
if (pageOptionsHome) pageOptionsHome.SetActive(false);
if (pageVolume) pageVolume.SetActive(true);
if (firstOnVolume) EventSystem.current?.SetSelectedGameObject(firstOnVolume.gameObject);
}
}
③ インスペクタ配線(手順)
- OptionPanel に
OptionsNavigator
をアタッチ。 pageOptionsHome
←PauseCanvas/OptionPanel/Page_OptionsHome
pageVolume
←PauseCanvas/OptionPanel/Panel_Volume
firstOnHome
←SoundOption
(Button)firstOnVolume
←Panel_Volume/Slider_Master
(任意の Slider)- SoundOption (Button) の On Click() に OptionPanel(OptionsNavigator付き)をドラッグ →
OptionsNavigator.ShowVolume()
を選択。 - BackButton (Button) の On Click() に OptionPanel をドラッグ →
OptionsNavigator.ShowHome()
を選択。
④ 役割の切り分け(重複しない)
- PauseManager:
PausePanel
とOptionPanel
の表示制御(開閉)。 - OptionsNavigator:
OptionPanel
内のページ(Page_OptionsHome
とPanel_Volume
)切替のみ。
触る階層が異なるため競合しません。
⑤ つまずきチェック
- SoundOption を押しても遷移しない: Button の On Click() が
OptionsNavigator.ShowVolume()
に配線されているか。 - Panel_Volume が見えない:
ShowVolume()
で Active 化されるか/RectTransform が画面外や別 Canvas でないか。 - Esc でオプションが閉じない: それは PauseManager 側の Update ロジック(既存)を確認。
⑥ おまけ:初回フォーカスの推奨
ゲームパッド操作対応なら、firstOnHome
を SoundOption
に、firstOnVolume
を最初に動かしたい Slider
に設定しておくとUXが良いです。
以上で、OptionPanel → Panel_Volume(音量スライダー) への遷移が動作し、PauseManager とは責務が重複しません。Display / Key などのページも同じパターンで増やせます(pageDisplay
などを追加)。
PauseCanvas > OptionPanel にOptionsNavigator.csをアタッチ
OptionsNavigator.csの役割は
・画面を開いたときにどこを選択状態にするか
・画面の表示・非表示に切り替え
今回は
・オプション画面を開いたときは音量設定のボタンを最初に選択状態にする
・音量設定画面では全体のスライダーを最初に選択状態にする
・オプション画面を開いたときはオプション画面のみを表示状態にする。
・音量設定画面を開いたときは音量設定画面のみを表示状態にする。
今後、画面設定などを追加するときはここにコードを追加しないといけなさそうです。