オーディオ

プールされたソースとポーズ対応フェードを備えた、Unity AudioMixer 経由の音楽・効果音・ボイス再生。

3 つの AudioMixer チャンネルで音楽・効果音・ボイスを再生 · プールされたソース、自動フェード、ポーズ対応の挙動 · no-op 代替: NullAudio

CGS はゲーム内のすべてのサウンドを IAudioService 経由でルーティングします。Unity AudioMixer 上の 3 つの専用チャンネル — Music(ループ再生の BGM トラック)、Sfx(ワンショットの効果音。2D または 3D 空間に配置)、Voice(台詞やアナウンス。同時に 1 クリップのみ)— を使います。プールされた AudioSource コンポーネントにより、効果音が連続して発生してもガベージコレクションのスパイクは起きません。フェードとクロスフェードは組み込み済みで、ポーズはプレイヤーの期待どおりに動作します — 音楽は流れ続け、ゲームプレイの効果音は停止します。ネットワーク呼び出しなし、サードパーティプラグインなし、IL2CPP セーフです。

できること

  • 自動フェード付きの音楽再生。 PlayMusic(clip) はトラックをフェードインします。別のクリップで再度呼び出すと、ぶつ切りにせずクロスフェードします。StopMusic() はフェードアウトします。
  • プールされた効果音。 PlaySfx(clip) は事前確保されたプールからソースを取り出し、繰り返し再生しても自然に聞こえるようわずかなランダムピッチ変動(デフォルトで約 ±10%)を適用し、クリップの終了時にソースをプールへ返却します。すべてのソースが使用中の場合は、最も古い再生中の効果音を停止して空きを作ります。
  • 同時 1 本のボイス。 PlayVoice(clip) は再生中のボイスを停止して新しいものを開始します — 台詞が重なって再生されることはありません。
  • ミキサースナップショット。 スナップショットとは、保存されたミキサー音量設定のセットです。通常のゲームプレイ、台詞中の音楽ダッキング、メニュー背後のこもった音など、プリセット間を 1 回の呼び出しと滑らかなフェードで遷移できます。
  • リアルタイムの音量設定。 Configuration サービス経由で接続した音量スライダーは、リアルタイムでミキサーに反映されます。

サービスの取得

[DefaultExecutionOrder(100)] を付けたクラスの Awake で解決し、キャッシュします:

using CommonGameSystem.Core;
using UnityEngine;

[DefaultExecutionOrder(100)]
public class SoundManager : MonoBehaviour
{
    private IAudioService _audio;

    private void Awake()
    {
        _audio = ServiceLocator.Resolve<IAudioService>();
    }
}

Update() の中で Resolve を呼んではいけません — この解決処理はディクショナリ検索です。一度だけキャッシュしてください。

API リファレンス

BGM

void PlayMusic(AudioClip clip, float fadeInSeconds = -1f, float crossfadeSeconds = -1f)

ループトラックをフェードインで開始します。すでにトラックが再生中の場合は、代わりに crossfadeSeconds かけて新しいトラックへクロスフェードします。-1f を渡すとデフォルト値(フェードイン 1.5 秒、クロスフェード 1.0 秒)を使用し、0f を渡すと即座に切り替わります。

void StopMusic(float fadeOutSeconds = -1f) // Fade out and stop (default 2.0 seconds)
bool IsMusicPlaying { get; }               // true while a track is playing

効果音(2D・3D)

bool PlaySfx(AudioClip clip, float pitchVariation = -1f)
bool PlaySfx(AudioClip clip, Vector3 worldPosition, float pitchVariation = -1f)

最初のオーバーロードはフラットな 2D 効果音を再生し、2 つ目はワールド座標で距離ベースのロールオフ付き 3D 効果音を再生します。どちらも成功時に true を返し、クリップが null、未ロード、またはプールがリクエストを拒否した場合は false を返します。pitchVariation はランダムピッチの半径です: -1f はデフォルト(約 ±10%)を使用、0f は変動を無効化、0.5f を超える値は警告付きでクランプされます。

int ActiveSfxCount { get; } // Number of effect sources currently playing

ボイスと台詞

void PlayVoice(AudioClip clip) // Starts the clip; any prior voice clip stops immediately
void StopVoice()               // Stops the current voice clip
bool IsVoicePlaying { get; }   // true while voice is playing

ミキサースナップショットと全体停止

void SetSnapshot(AudioSnapshot snapshot, float transitionSeconds = -1f)
void SetSnapshot(string snapshotName, float transitionSeconds = -1f)
void StopAll()

enum オーバーロードは組み込みプリセット — NormalDialogueDucked(台詞中に音楽と効果音を約 6 dB 下げる)、MenuOpen — のいずれかへ遷移します。string オーバーロードは、独自のミキサーに追加した任意のスナップショットを対象にできます。StopAll() はフェードなしで全チャンネルを即座に無音化します。

完全な例

using CommonGameSystem.Core;
using UnityEngine;

[DefaultExecutionOrder(100)]
public class GameAudio : MonoBehaviour
{
    [SerializeField] private AudioClip _explosionClip;

    private IAudioService _audio;

    private void Awake()
    {
        _audio = ServiceLocator.Resolve<IAudioService>();
    }

    public void PlayBattleMusic(AudioClip clip)
    {
        // Fade in over 1.5 seconds; if music is already playing,
        // the service crossfades to the new track automatically.
        _audio.PlayMusic(clip, fadeInSeconds: 1.5f);
    }

    public void PlayExplosion(Vector3 position)
    {
        // 3D effect with the default pitch variation (about ±10%)
        _audio.PlaySfx(_explosionClip, position);
    }

    public void DialogueStart()
    {
        // Duck music and effects while dialogue plays
        _audio.SetSnapshot(AudioSnapshot.DialogueDucked);
    }

    public void PlayDialogue(AudioClip voiceClip)
    {
        _audio.PlayVoice(voiceClip);
    }

    public void DialogueEnd()
    {
        _audio.SetSnapshot(AudioSnapshot.Normal);
    }
}

無効化する

ServiceLocator.Replace<IAudioService>(new NullAudio());

この 1 行でサービスを無音化できます。すべてのメソッドが no-op になり、ゲームコードの変更は不要です。テスト、ボイス収録セッション、独自のオーディオミドルウェアを使う場合に便利です。AudioListener には影響しません — カメラには引き続き必要ですが、そこから何も再生されなくなります。

よくある落とし穴

  • メインスレッド専用。 PlayMusicPlaySfxPlayVoiceSetSnapshot の呼び出しはすべてメインスレッドで行う必要があります。ワーカースレッドからサウンドを鳴らしたい場合は、先に呼び出しをメインスレッドへマーシャリングしてください — Scheduler が代行できます。

  • ポーズ時の挙動は組み込み済み。 音楽のフェードは Time サービスの Background クロック上で動くため、ゲームプレイがポーズしても音楽は流れ続けます。効果音のタイマーは Gameplay クロック上で動くため、効果音は停止します。「音楽は続き、効果音は止まる」ための追加コードは不要です。

  • 負荷の高いシーンの前にプールをプリウォームする。 デフォルトでは起動時に 8 つの効果音ソースが事前確保されます。最初の戦闘シーンで爆発が一度に 20 発鳴ると、プールがオンデマンドで拡張されフレームヒッチの原因になります。起動時に AudioOptions.sfxPoolPrewarmCount を増やして多めに事前確保してください — わずかな起動時メモリと引き換えに、実行時のカクつきをゼロにできます。

  • 音量スライダーの反映にはフラッシュが必要。 Configuration サービスは設定の書き込みをバッチ処理するため、Set(new AudioSettings { ... }) を呼んだだけでは変更はキューに入るだけで、ミキサーにはまだ届きません。スライダーのリアルタイムなフィードバックには、Set の直後に解決済みの IConfigurationFlushPending<AudioSettings>() を呼んでください。フラッシュすればミキサーは即座に更新されます。

  • カスタム AudioMixer のパラメーター名。 AudioOptions 経由で独自の AudioMixer を渡す場合、MasterVolumeMusicVolumeSfxVolumeVoiceVolume の 4 つのパラメーター(いずれも dB 単位)を公開している必要があります。名前が異なる場合は AudioOptions.masterParammusicParamsfxParamvoiceParam でサービスに指定してください。

  • 効果音のクリーンアップはクリップ長で決まる。 PlaySfx(clip) の後、ソースは clip.length 秒(unscaled time で計測)経過後に自動でプールへ戻ります — スローモーションでクリーンアップが遅れることはありません。手動の管理は不要です。

  • カスタム型は IL2CPP 向けに link.xml が必要。 オーディオ設定を保持する独自の設定型を追加する場合は、IL2CPP にストリップされないよう link.xml エントリーを追加してください:

    <assembly fullname="Assembly-CSharp">
      <type fullname="YourGame.CustomAudioConfig" preserve="all" />
    </assembly>
    

関連ページ

  • Time — ポーズ対応フェードを支えるクロック
  • Configuration — 設定グループと FlushPending
  • Scheduler — メインスレッドへの呼び出しのマーシャリング