【Cocos Creator】アタッチするだけ!AmbientFader (環境音調整)の実装方法【TypeScript】

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

【Cocos Creator 3.8】AmbientFader の実装:アタッチするだけで「洞窟・水中などの環境音のボリューム&リバーブを自動調整」する汎用スクリプト

このガイドでは、ノードにアタッチするだけで、プレイヤーが洞窟や水中エリアに入ったときのような環境音のボリュームとリバーブ量を自動でフェードさせるコンポーネント AmbientFader を実装します。

シーンごとにサウンドマネージャーを用意したり、複雑なシングルトンを組む必要はありません。1つの AudioSource に対してこのコンポーネントを付けておくだけで、トリガー呼び出しに応じて環境音を「通常状態 ⇔ 洞窟状態」などへ滑らかに切り替えられます。


コンポーネントの設計方針

1. 機能要件の整理

  • このコンポーネントがアタッチされたノードにある AudioSource
    • ボリューム(volume
    • 簡易的な「リバーブ量」を表すパラメータ(reverbLevel

    を、時間経過でなめらかにフェードさせる。

  • 他のカスタムスクリプトには一切依存しない
    • 必要な情報はすべて @property でインスペクタから設定。
    • 「洞窟に入った」「水中に入った」といったイベントは、enterEnvironment() / exitEnvironment() メソッドを他スクリプトやトリガーコリジョンから呼ぶだけで完結できるようにする。
  • AudioSource がアタッチされていない場合は、自動取得を試み、失敗したらエラーログを出す(防御的実装)。
  • リバーブは Cocos 標準の直接パラメータがないため、ここでは「0〜1 の任意値」として管理し、
    • インスペクタから reverbLevel を実行時に参照できるようにする。
    • 実際のリバーブ処理は、必要であれば他のエフェクト処理側でこの値を参照して行えるようにする。

2. 状態とフェード挙動

AmbientFader は、以下の 2 つの状態間をフェードします。

  • 通常環境(Normal)
    • 通常ボリューム:normalVolume
    • 通常リバーブ量:normalReverb
  • 特殊環境(Ambient / Cave / Water など)
    • 環境ボリューム:ambientVolume
    • 環境リバーブ量:ambientReverb

環境切り替えは、次のメソッド呼び出しで行います。

  • enterEnvironment():Normal → Ambient へフェード
  • exitEnvironment():Ambient → Normal へフェード

フェード時間は fadeDuration(秒)で統一的に制御し、update() 内で Lerp 補間します。

3. インスペクタで設定可能なプロパティ

AmbientFader に定義する @property とその役割は以下の通りです。

  • autoPlayOnLoad: boolean
    • ツールチップ: 「onLoad 時に AudioSource を自動再生するか」
    • true の場合、コンポーネントが有効化されたタイミングで BGM/環境音を再生開始。
  • startInAmbient: boolean
    • ツールチップ: 「開始時の状態を Ambient(特殊環境)にするか」
    • true の場合、初期状態を Ambient として設定し、ambientVolume / ambientReverb を適用。
  • fadeDuration: number
    • ツールチップ: 「Normal と Ambient の状態を切り替えるときのフェード時間(秒)」
    • 例: 1.0〜2.0 秒程度が自然。
  • normalVolume: number
    • ツールチップ: 「通常環境での AudioSource のボリューム(0〜1)」
    • 例: 0.8
  • ambientVolume: number
    • ツールチップ: 「洞窟・水中などの特殊環境でのボリューム(0〜1)」
    • 例: 洞窟なら 0.6、水中なら 0.4 など。
  • normalReverb: number
    • ツールチップ: 「通常環境でのリバーブ量(0〜1)」
    • 実際のリバーブ処理は別途だが、この値を基準として扱う。
  • ambientReverb: number
    • ツールチップ: 「特殊環境でのリバーブ量(0〜1)」
    • 洞窟なら 0.7〜1.0、水中なら 0.5〜0.8 など、好みで調整。
  • logWarnings: boolean
    • ツールチップ: 「AudioSource 未設定などの警告ログを出すか」
    • 開発中は true 推奨、本番でログを減らしたければ false に。

また、エディタからは直接触らないが、現在のリバーブ量を確認するための読み取り専用プロパティとして、

  • currentReverb: number
    • ツールチップ: 「現在のリバーブ量(0〜1)。AmbientFader が内部で管理する値」
    • 他コンポーネントが、getComponent(AmbientFader)?.currentReverb で参照する用途を想定。

TypeScriptコードの実装


import { _decorator, Component, AudioSource, clamp01, warn, log } from 'cc';
const { ccclass, property } = _decorator;

/**
 * AmbientFader
 * 環境音のボリュームと簡易リバーブ量を、Normal <-> Ambient 間でフェードさせるコンポーネント。
 *
 * - このスクリプトを AudioSource を持つノードにアタッチするだけで動作します。
 * - enterEnvironment() / exitEnvironment() を呼び出すと、指定時間でボリュームとリバーブ量が補間されます。
 */
@ccclass('AmbientFader')
export class AmbientFader extends Component {

    @property({
        tooltip: 'onLoad 時に AudioSource を自動再生するかどうか。',
    })
    public autoPlayOnLoad: boolean = true;

    @property({
        tooltip: '開始時の状態を Ambient(特殊環境)にするかどうか。\ntrue の場合、ambientVolume / ambientReverb から開始します。',
    })
    public startInAmbient: boolean = false;

    @property({
        tooltip: 'Normal と Ambient の状態を切り替えるときのフェード時間(秒)。\n0 の場合は即時切り替え。',
        min: 0,
    })
    public fadeDuration: number = 1.0;

    @property({
        tooltip: '通常環境での AudioSource のボリューム(0〜1)。',
        min: 0,
        max: 1,
    })
    public normalVolume: number = 0.8;

    @property({
        tooltip: '特殊環境(洞窟・水中など)での AudioSource のボリューム(0〜1)。',
        min: 0,
        max: 1,
    })
    public ambientVolume: number = 0.5;

    @property({
        tooltip: '通常環境でのリバーブ量(0〜1)。\n実際のリバーブ処理は別途実装し、この値を参照してください。',
        min: 0,
        max: 1,
    })
    public normalReverb: number = 0.0;

    @property({
        tooltip: '特殊環境でのリバーブ量(0〜1)。\n洞窟なら 0.7〜1.0、水中なら 0.5〜0.8 などに設定すると雰囲気が出ます。',
        min: 0,
        max: 1,
    })
    public ambientReverb: number = 0.8;

    @property({
        tooltip: 'AudioSource 未設定などの警告ログを出すかどうか。',
    })
    public logWarnings: boolean = true;

    @property({
        tooltip: '現在のリバーブ量(0〜1)。AmbientFader が内部で管理する値です。',
        readonly: true,
    })
    public currentReverb: number = 0.0;

    // 内部状態管理用
    private _audioSource: AudioSource | null = null;

    private _isInAmbient: boolean = false;
    private _isFading: boolean = false;
    private _fadeElapsed: number = 0;
    private _fadeFromVolume: number = 0;
    private _fadeToVolume: number = 0;
    private _fadeFromReverb: number = 0;
    private _fadeToReverb: number = 0;

    onLoad() {
        // 必要な AudioSource を取得
        this._audioSource = this.getComponent(AudioSource);

        if (!this._audioSource) {
            if (this.logWarnings) {
                warn('[AmbientFader] AudioSource がノードに見つかりません。' +
                    'このコンポーネントを使用するには、同じノードに AudioSource コンポーネントを追加してください。');
            }
            return;
        }

        // 初期状態を設定
        this._isInAmbient = this.startInAmbient;

        const startVolume = this._isInAmbient ? this.ambientVolume : this.normalVolume;
        const startReverb = this._isInAmbient ? this.ambientReverb : this.normalReverb;

        this.normalVolume = clamp01(this.normalVolume);
        this.ambientVolume = clamp01(this.ambientVolume);
        this.normalReverb = clamp01(this.normalReverb);
        this.ambientReverb = clamp01(this.ambientReverb);

        this._audioSource.volume = clamp01(startVolume);
        this.currentReverb = clamp01(startReverb);

        if (this.autoPlayOnLoad && !this._audioSource.playing) {
            this._audioSource.play();
        }

        if (this.logWarnings) {
            log('[AmbientFader] Initialized. startInAmbient =', this.startInAmbient,
                ', volume =', this._audioSource.volume,
                ', reverb =', this.currentReverb);
        }
    }

    update(deltaTime: number) {
        if (!this._audioSource) {
            // AudioSource がない場合は何もしない
            return;
        }

        if (!this._isFading) {
            return;
        }

        if (this.fadeDuration <= 0) {
            // フェード時間 0 の場合は即時反映
            this._audioSource.volume = clamp01(this._fadeToVolume);
            this.currentReverb = clamp01(this._fadeToReverb);
            this._isFading = false;
            return;
        }

        this._fadeElapsed += deltaTime;
        let t = this._fadeElapsed / this.fadeDuration;
        if (t >= 1) {
            t = 1;
            this._isFading = false;
        }

        // 線形補間
        const newVolume = this._lerp(this._fadeFromVolume, this._fadeToVolume, t);
        const newReverb = this._lerp(this._fadeFromReverb, this._fadeToReverb, t);

        this._audioSource.volume = clamp01(newVolume);
        this.currentReverb = clamp01(newReverb);
    }

    /**
     * 環境(洞窟・水中など)に「入った」ときに呼び出すメソッド。
     * Normal -> Ambient へフェードします。
     */
    public enterEnvironment(): void {
        this._setTargetState(true);
    }

    /**
     * 環境(洞窟・水中など)から「出た」ときに呼び出すメソッド。
     * Ambient -> Normal へフェードします。
     */
    public exitEnvironment(): void {
        this._setTargetState(false);
    }

    /**
     * 現在 Ambient 環境かどうかを返します。
     */
    public get isInAmbient(): boolean {
        return this._isInAmbient;
    }

    /**
     * 内部的に Normal/Ambient のどちらかに遷移するための共通処理。
     */
    private _setTargetState(toAmbient: boolean): void {
        if (!this._audioSource) {
            if (this.logWarnings) {
                warn('[AmbientFader] enterEnvironment/exitEnvironment が呼ばれましたが、AudioSource が見つかりません。');
            }
            return;
        }

        const fromVolume = this._audioSource.volume;
        const fromReverb = this.currentReverb;

        const toVolume = toAmbient ? this.ambientVolume : this.normalVolume;
        const toReverb = toAmbient ? this.ambientReverb : this.normalReverb;

        this._fadeFromVolume = clamp01(fromVolume);
        this._fadeToVolume = clamp01(toVolume);
        this._fadeFromReverb = clamp01(fromReverb);
        this._fadeToReverb = clamp01(toReverb);

        this._fadeElapsed = 0;
        this._isFading = true;
        this._isInAmbient = toAmbient;
    }

    /**
     * 線形補間
     */
    private _lerp(a: number, b: number, t: number): number {
        return a + (b - a) * t;
    }
}

コードのポイント解説

  • onLoad()
    • getComponent(AudioSource) で同じノードの AudioSource を取得。
    • 見つからなければ warn でエラーメッセージを出し、それ以降の処理はスキップ。
    • startInAmbient に応じて初期ボリューム&リバーブを設定。
    • autoPlayOnLoadtrue なら AudioSource.play() を自動実行。
  • update(deltaTime)
    • _isFadingtrue の間、_fadeElapsed を増加させてフェード進行度 t を計算。
    • _lerp 関数でボリュームとリバーブ量を補間し、AudioSource と currentReverb に反映。
    • fadeDuration <= 0 の場合は即時切り替え。
  • enterEnvironment / exitEnvironment
    • 外部から呼び出す公開 API。
    • 内部で _setTargetState(true/false) を呼び、フェードの開始パラメータを設定。
  • currentReverb プロパティ
    • AmbientFader 内部で管理される「現在のリバーブ量」。
    • 別のコンポーネントが getComponent(AmbientFader)?.currentReverb を参照することで、
      ポストエフェクトやカスタムオーディオ処理と連携できるようにしてあります。

使用手順と動作確認

1. スクリプトファイルの作成

  1. Editor の Assets パネルで右クリックします。
  2. Create → TypeScript を選択します。
  3. ファイル名を AmbientFader.ts に変更します。
  4. 作成された AmbientFader.ts をダブルクリックしてエディタ(VS Code など)で開き、
    上記の AmbientFader のコードを丸ごと貼り付けて保存します。

2. テスト用ノードと AudioSource の準備

  1. Hierarchy パネルで右クリック → Create → Empty Node を選択し、
    ノード名を AmbientAudio などに変更します。
  2. AmbientAudio ノードを選択し、Inspector を確認します。
  3. Add Component → Audio → AudioSource を選択して、
    AudioSource コンポーネントを追加します。
  4. AudioSource の Clip に、洞窟の環境音や水の揺らぎ音など、
    ループさせたいサウンドを割り当てます。
  5. AudioSource の Loop チェックボックスを ON にしておきましょう。

ここまでで、「環境音を再生するノード」が準備できました。

3. AmbientFader コンポーネントのアタッチ

  1. 再び AmbientAudio ノードを選択します。
  2. Inspector の下部で Add Component → Custom → AmbientFader を選択し、
    作成したコンポーネントをアタッチします。
  3. Inspector に AmbientFader セクションが表示されていることを確認します。

4. プロパティの設定例

洞窟に入ったときに「少しボリュームを絞り、リバーブを強くする」例を示します。

  • autoPlayOnLoad: true
    • シーン開始と同時に環境音を再生したい場合。
  • startInAmbient: false
    • 最初は通常環境から始める。
  • fadeDuration: 1.5
    • 1.5 秒かけてゆっくり変化させる。
  • normalVolume: 0.8
    • 屋外など通常環境の音量。
  • ambientVolume: 0.6
    • 洞窟内で少し抑えた音量。
  • normalReverb: 0.1
    • ほとんど残響のない状態。
  • ambientReverb: 0.8
    • 洞窟内で残響が強くなるイメージ。
  • logWarnings: true

5. シンプルな動作確認方法(エディタだけで確認)

スクリプト呼び出しなしで挙動を確認するには、以下のようにします。

  1. シーンを保存し、Editor 上部の「Play」ボタンでゲームを再生します。
  2. ゲーム実行中に AmbientAudio ノードを選択し、
    Inspector の AmbientFader コンポーネントを確認します。
  3. 実行中に startInAmbient は変更しても反映されませんが、
    代わりに AmbientFader の公開メソッドを呼び出して確認します。
    • 簡単な方法として、別のテスト用スクリプトを作成し、
      enterEnvironment() / exitEnvironment() をキー入力で呼び出すなどが考えられます。

例として、同じノードに一時的に以下のようなテストスクリプトを追加すると、キーボードで切替を確認できます(本ガイドのコンポーネント自体は他スクリプトに依存していない点に注意してください)。


// ※テスト用途。AmbientFader 本体には含めません。
import { _decorator, Component, input, Input, KeyCode, EventKeyboard } from 'cc';
const { ccclass } = _decorator;

@ccclass('AmbientFaderTester')
export class AmbientFaderTester extends Component {
    private _fader: any;

    onLoad() {
        this._fader = this.getComponent('AmbientFader');
        input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
    }

    onDestroy() {
        input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
    }

    onKeyDown(event: EventKeyboard) {
        if (!this._fader) return;
        if (event.keyCode === KeyCode.KEY_C) {
            this._fader.enterEnvironment();
        } else if (event.keyCode === KeyCode.KEY_V) {
            this._fader.exitEnvironment();
        }
    }
}

ゲーム再生中に C キーで洞窟に入る(Ambient)V キーで外に出る(Normal) という形で、
音量とリバーブ量の変化を確認できます。

6. 実運用での連携例

実際のゲームでは、プレイヤーがトリガーコリジョンに入ったタイミングで enterEnvironment() を呼び出すと自然です。

  • 「洞窟入口」コライダーに、プレイヤーが入ったら AmbientAudio ノードの
    AmbientFader.enterEnvironment() を呼ぶスクリプトを付与。
  • 「洞窟出口」コライダーで exitEnvironment() を呼ぶ。

このように、AmbientFader 自体は完全に独立したコンポーネントであり、どのノードに付けても同じように使えるように設計されています。


まとめ

  • AmbientFader は、AudioSource を持つノードにアタッチするだけで、
    環境音のボリュームとリバーブ量を Normal ⇔ Ambient でフェードできる汎用コンポーネントです。
  • 必要なパラメータはすべて @property でインスペクタから設定でき、
    他のカスタムスクリプトやシングルトンに一切依存しません
  • enterEnvironment() / exitEnvironment() を呼び出すだけで、
    洞窟・水中・屋内などさまざまな環境の切り替え演出を簡単に実装できます。
  • currentReverb を外部から参照することで、
    ポストエフェクトやカスタムオーディオ処理と連携した高度なサウンド演出も可能です。

このような「アタッチするだけで完結する汎用コンポーネント」を積み重ねていくことで、
シーンごとの管理コードを減らし、ゲーム開発の再利用性とスピードを大きく向上させることができます。AmbientFader をベースに、さらに EQ やローパスフィルタ風のパラメータなどを追加して、自分のプロジェクトに合った環境音システムへ発展させてみてください。

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

URLをコピーしました!