【Godot】アダプティブミュージック - 状況で変わるBGMをAudioStreamInteractiveで作る

作成: 2026-02-08最終更新: 2026-07-08

Godot 4.3+のAudioStreamInteractiveとAudioStreamSynchronizedで、ゲーム状況に応じて変化するアダプティブミュージックを実装する方法を解説。クリップ切替型とレイヤー重ね型、トランジションのタイミング、探索/戦闘BGM切り替えの実装まで、コードと図で学びます。

探索中はゆったりしたBGM、敵と戦い始めた瞬間に激しい曲へ切り替わり、ボスが出るとさらに盛り上がる——名作ゲームのあの 「音楽が状況に反応する」 演出。同じBGMを延々ループさせるだけだと、どうしても場面の緊張感が出せません。

この動的な音楽制御を アダプティブミュージック と呼び、Godot 4.3以降では AudioStreamInteractiveAudioStreamSynchronized で実装できます。この記事では、2つのアプローチの違いから、切り替えタイミングの設計、探索⇄戦闘を切り替えるBGMマネージャーの実装までを組み立てます。

穏やかな波形が右へ進むほど激しい波形に変化していくアダプティブミュージックのイメージ。音符が散りばめられている

この記事でわかること

  • アダプティブミュージックの 2つのアプローチ(クリップ切替型 / レイヤー重ね型)
  • AudioStreamInteractive状態に応じてBGMクリップを切り替える
  • 切り替えの タイミング(即座・曲末・次の拍)の使い分け
  • AudioStreamSynchronizedパートを足し引き するレイヤー型
  • 探索⇄戦闘のBGMマネージャー を1プレイヤーで組む

Sponsored

2つのアプローチ:クリップ切替とレイヤー重ね

アダプティブミュージックには、大きく2つの作り方があります。どちらを選ぶかで使うクラスが変わります。

クリップ切替型は穏やかな曲を激しい曲へ丸ごと入れ替え、レイヤー重ね型はドラム・ベースにメロディを後から重ねる、2つのアプローチの対比図
  • クリップ切替型 :探索用・戦闘用など、 曲そのものを丸ごと入れ替える 。場面ごとに全く違う曲を用意でき、変化がはっきりします。→ AudioStreamInteractive
  • レイヤー重ね型 :同じ曲の パート(ドラム・ベース・メロディ)を足し引き する。敵が増えたらドラムを足す、といった段階的で滑らかな盛り上げが得意です。→ AudioStreamSynchronized

どちらか一方だけでなく、 併用 もできます(切り替えた各クリップの中身をレイヤー型にする)。まずはそれぞれを見ていきましょう。

AudioStreamInteractive:曲を切り替える

AudioStreamInteractive は、 複数の音楽クリップを状態に応じて切り替える リソースです。探索・戦闘・ボスといった状態を1つのリソースにまとめ、ゲームの状況が変わるたびに対応するクリップへ遷移します。

探索⇄戦闘→ボスの状態遷移図。敵と遭遇で戦闘へ、撃破で探索へ、ボス出現でボスへ。状態が変わるとBGMクリップが切り替わる

AudioStreamInteractive は、GDScriptからの構築APIが限定的なため、 エディタのインスペクタで作るのが基本 です。

  1. FileSystemで右クリック →「新規リソース」→ AudioStreamInteractive
  2. Clip Count を設定し、各クリップに名前とAudioStreamを割り当て(例:explorationcombat
  3. Transitions で遷移のタイミングを設定
  4. .tres として保存

スクリプト側は、作ったリソースを読み込んで再生するだけです。

# エディタで作った .tres を読み込んで再生
var music := preload("res://audio/bgm_interactive.tres")
$AudioStreamPlayer.stream = music
$AudioStreamPlayer.play()

切り替えのタイミング

AudioStreamInteractive の強みは、 クリップの切り替わり方を細かく選べる ことです。同じ「戦闘へ切り替え」でも、タイミング1つで体感が大きく変わります。

切り替えタイミングの3種類。IMMEDIATEはすぐ、ENDは曲の終わり、NEXT BEATは次の拍で切り替わるタイムライン図
タイミング定数使いどころ
すぐ切り替えTRANSITION_FROM_TIME_IMMEDIATE戦闘開始など、即座に緊張感を出したいとき
曲の終わりでTRANSITION_FROM_TIME_END戦闘終了→探索など、自然に戻したいとき
次の拍でTRANSITION_FROM_TIME_NEXT_BEATリズムを崩さず滑らかに切り替えたいとき

戦闘開始は IMMEDIATE でパッと切り替え、戦闘終了は END で曲を締めてから戻す——このように 入りは即座・戻りは自然 に使い分けると、音楽が場面にきれいに寄り添います。

Sponsored

AudioStreamSynchronized:レイヤーを重ねる

もう1つのアプローチが、 同じ曲のパートを増減させる レイヤー型です。AudioStreamSynchronized は複数トラックを ぴったり同期して同時再生 し、各トラックの音量を個別に制御できます。

探索ではドラムとベースだけ、戦闘開始でメロディが加わる。メロディを重ねて盛り上げるレイヤー型音楽の図

まずドラム・ベース・メロディの3トラックを用意し、メロディだけミュートで始めます。

var layered := AudioStreamSynchronized.new()
layered.stream_count = 3
layered.set_sync_stream(0, drums_track)   # ドラム
layered.set_sync_stream(1, bass_track)    # ベース
layered.set_sync_stream(2, melody_track)  # メロディ

layered.set_sync_stream_volume(0, 0.0)    # ドラム:通常
layered.set_sync_stream_volume(1, 0.0)    # ベース:通常
layered.set_sync_stream_volume(2, -80.0)  # メロディ:ほぼ無音(後で上げる)

$AudioStreamPlayer.stream = layered
$AudioStreamPlayer.play()

戦闘に入ったら、メロディの音量を Tween でじわっと上げます。いきなり足すと唐突なので、フェードで重ねるのがコツです。

# 戦闘開始:メロディレイヤーをフェードイン
func start_combat() -> void:
    var music := $AudioStreamPlayer.stream as AudioStreamSynchronized
    # -80dB(無音)→0dB(通常)へ1秒でフェード
    create_tween().tween_method(
        func(db): music.set_sync_stream_volume(2, db), -80.0, 0.0, 1.0)

全パートが同じタイミングで鳴っているので、音量を上げ下げするだけで ズレずに レイヤーを足し引きできます。これがレイヤー型の気持ちよさです。

実践:探索⇄戦闘のBGMマネージャー

クリップ切替型を使って、実際のゲームで使えるBGMマネージャーを組んでみましょう。アクションRPGの探索⇄戦闘、ローグライクのフロア⇄ボス、ステルスの通常⇄発見——「2つの状態を行き来する」構造はジャンルを問わず同じ形で作れます。

1つのAudioStreamPlayerを持つBGMManagerが、enter_combat()/exit_combat()で探索BGMと戦闘BGMを切り替える図

ポイントは、 BGM用の AudioStreamPlayer は1つだけ に保ち、再生中のplaybackからクリップを切り替えることです。Autoload にして、どのシーンからでも呼べるようにしておくと便利です。

extends Node

@onready var music_player: AudioStreamPlayer = $AudioStreamPlayer
var current_state := "exploration"

func _ready() -> void:
    music_player.stream = preload("res://audio/bgm_interactive.tres")
    music_player.play()

func enter_combat() -> void:
    if current_state != "combat":      # 同じ状態への無駄な切り替えを防ぐ
        switch_to("combat")
        current_state = "combat"

func exit_combat() -> void:
    if current_state != "exploration":
        switch_to("exploration")
        current_state = "exploration"

func switch_to(clip_name: String) -> void:
    # 再生中のplaybackを取得し、名前でクリップを切り替える
    var playback := music_player.get_stream_playback() as AudioStreamPlaybackInteractive
    playback.switch_to_clip_by_name(clip_name)

ポイントは2つです。

  • プレイヤーは1つ、状態で中身を切り替える :BGMごとに AudioStreamPlayer を増やすと、二重再生や消し忘れの温床になります。1プレイヤー+switch_to_clip_by_name() に集約するのが鉄則です。
  • 現在の状態を覚えておくcurrent_state で今のBGMを記録し、 同じ状態への切り替えをスキップ します。これを怠ると、戦闘中に敵と遭遇するたびBGMが頭から鳴り直してしまいます。

エディタ側では、探索→戦闘を IMMEDIATE、戦闘→探索を END に設定しておくと、入りは鋭く・戻りは自然になります。

Sponsored

ベストプラクティス

  • BGMは1プレイヤーに集約AudioStreamPlayer を1つに保ち、switch_to_clip_by_name() でクリップを替える
  • 入りは即座・戻りは自然 :戦闘開始は IMMEDIATE、終了は END でトランジションを設計する
  • フェードで滑らかに :レイヤーの増減やクリップ音量は、0.5〜1.0秒のフェードで急変を避ける
  • 状態を明確に管理current_state で現在のBGMを持ち、 同じ状態への無駄な切り替え を防ぐ
  • クリップ型とレイヤー型は併用可 :各クリップの中身を AudioStreamSynchronized にすれば、状態切替と段階的盛り上げを両立できる

おまけ:先に知っておくと良いこと

  • 単純な連続再生なら AudioStreamPlaylist :BGMを順番/ランダムに流すだけなら、遷移設定のないこちらがシンプルです。状態遷移が要るなら AudioStreamInteractive を選びます。
  • エディタで作り、コードは再生制御に集中AudioStreamInteractive はインスペクタでの構築が前提です。GDScriptは「いつ・どのクリップへ」の指示だけに絞ると見通しが良くなります。
  • 切り替えのきっかけは シグナル :敵の発見やボスの出現を各所から AudioManager に伝えるには、状態変化をシグナルで飛ばすと疎結合に保てます。
  • バス構成やエフェクトは基礎編へ :BGMバスの音量やフェードは オーディオ管理の基礎AudioBusエフェクト が土台になります。

まとめ

  • 2アプローチAudioStreamInteractive(曲まるごと切替)と AudioStreamSynchronized(パートを足し引き)
  • トランジションIMMEDIATE(即座)・END(曲末)・NEXT_BEAT(拍)を場面で使い分ける
  • レイヤー型 は各トラックの音量を Tween でフェードして重ねる
  • 実践 :1つの AudioStreamPlayerswitch_to_clip_by_name() で状態ごとにクリップを切替、current_state で無駄な切り替えを防ぐ

まずは探索用と戦闘用の2曲で AudioStreamInteractive を作り、IMMEDIATEEND の違いを耳で確かめてみてください。音楽が場面に反応し始めると、ゲームの手応えが一段深くなります。

さらに学ぶために