🎵
【Unity】動的にAudioFilterを適用,無効化したい【C#】
背景
- 開発サウンドライブラリでプリセット機能(ロード方法の指定とか)を実装していた
- 実行時に使用する AudioFilter を予めプリセットで設定できる機能が欲しくなった
- しかし、そもそも AudioFilter を動的に AudioListener へ適用する機能がなかった
- 実装するぞ~
どんな風に実装していくのか
-
MonoBehaviourを継承しない- ツール系クラスとして 純粋な C# クラス に留める
- ライブラリ開発の事情もある
- オブジェクトにアタッチして使いたい方向けに
Monobehaviour継承版アリ↓
-
必須機能
- 追加(ApplyFilter)
- 無効化(DisableFilter)
- 破棄(RemoveFilter)
-
依存ライブラリは Unity 標準 API のみ
結果
- AudioListener に対し AudioFilter(Reverb 等)を動的に追加, 削除できるクラスを実装
Monobehaviour 継承版
using UnityEngine;
using System;
using System.Collections.Generic;
[DisallowMultipleComponent]
[RequireComponent(typeof(AudioListener))]
public class ListenerEffectorMB : MonoBehaviour
{
//フィルターの種類(AudioChorusFilter 等)を選ぶための列挙型
public enum FilterKind { Chorus, Distortion, Echo, HighPass, LowPass, Reverb }
[SerializeField, Tooltip("現在アタッチ済みフィルター(デバッグ表示用)")]
private List<string> activeFilters = new();
private readonly Dictionary<Type, Component> filterDict = new();
private AudioListener listener;
private void Awake()
{
listener = GetComponent<AudioListener>();
}
private void OnDisable() => DisableAllFilters(); //シーン停止時、音を切る
private void OnDestroy() => RemoveAllFilters();
//ジェネリック型で直接追加 + 設定
public void ApplyFilter<FilterT>(Action<FilterT> configure = null)
where FilterT : Behaviour
{
if (!filterDict.TryGetValue(typeof(FilterT), out var comp))
{
comp = listener.gameObject.AddComponent<FilterT>();
filterDict[typeof(FilterT)] = comp;
activeFilters.Add(typeof(FilterT).Name);
}
if (comp is FilterT filter)
{
filter.enabled = true;
configure?.Invoke(filter);
}
}
//無効化(指定フィルターのみ)
public void DisableFilter<FilterT>() where FilterT : Behaviour
{
if (filterDict.TryGetValue(typeof(FilterT), out var comp) &&
comp is FilterT filter)
{
filter.enabled = false;
}
}
//無効化(全フィルター)
public void DisableAllFilters()
{
foreach (var comp in filterDict.Values)
if (comp is Behaviour b) b.enabled = false;
}
//破棄(指定フィルターのみ)
public void RemoveFilter<FilterT>() where FilterT : Behaviour
{
if (filterDict.TryGetValue(typeof(FilterT), out var comp))
{
filterDict.Remove(typeof(FilterT));
activeFilters.Remove(typeof(FilterT).Name);
Destroy(comp);
}
}
//破棄(全フィルター)
public void RemoveAllFilters()
{
foreach (var comp in filterDict.Values)
{
if (comp) Destroy(comp);
}
filterDict.Clear();
activeFilters.Clear();
}
//FilterKind → 実際のクラス型 に変換するヘルパー関数
private static Type GetFilterType(FilterKind k) => k switch
{
FilterKind.Chorus => typeof(AudioChorusFilter),
FilterKind.Distortion => typeof(AudioDistortionFilter),
FilterKind.Echo => typeof(AudioEchoFilter),
FilterKind.HighPass => typeof(AudioHighPassFilter),
FilterKind.LowPass => typeof(AudioLowPassFilter),
FilterKind.Reverb => typeof(AudioReverbFilter),
_ => null,
};
}
using UnityEngine;
using System;
using System.Collections.Generic;
public sealed class ListenerEffector
{
//フィルターの種類(AudioChorusFilter 等)を選ぶための列挙型
public enum FilterKind { Chorus, Distortion, Echo, HighPass, LowPass, Reverb }
private AudioListener listener;
private readonly Dictionary<Type, Component> filterDict = new();
public ListenerEffector(AudioListener l) { listener = l; }
//ジェネリック型で直接追加 + 設定
public void ApplyFilter<FilterT>(Action<FilterT> configure = null)
where FilterT : Behaviour
{
if (!filterDict.TryGetValue(typeof(FilterT), out var component))
{
component = listener.gameObject.AddComponent<FilterT>();
filterDict[typeof(FilterT)] = component;
}
if (component is FilterT filter)
{
filter.enabled = true;
configure?.Invoke(filter);
}
}
//無効化(指定フィルターのみ)
public void DisableFilter<FilterT>() where FilterT : Behaviour
{
if (filterDict.TryGetValue(typeof(FilterT), out var component) &&
component is FilterT filter)
{
filter.enabled = false;
}
}
//無効化(全フィルター)
public void DisableAllFilters()
{
foreach (var component in filterDict.Values)
{
if (component is Behaviour b)
b.enabled = false;
}
}
//破棄(指定フィルターのみ)
public void RemoveFilter<FilterT>() where FilterT : Behaviour
{
if (filterDict.TryGetValue(typeof(FilterT), out var component))
{
filterDict.Remove(typeof(FilterT));
UnityEngine.Object.Destroy(component);
}
}
//破棄(全フィルター)
public void RemoveAllFilters()
{
foreach (var component in filterDict.Values)
{
if (component != null)
UnityEngine.Object.Destroy(component);
}
filterDict.Clear();
}
//FilterKind → 実際のクラス型 に変換するヘルパー関数
private Type GetFilterType(FilterKind kind)
{
return kind switch
{
FilterKind.Chorus => typeof(AudioChorusFilter),
FilterKind.Distortion => typeof(AudioDistortionFilter),
FilterKind.Echo => typeof(AudioEchoFilter),
FilterKind.HighPass => typeof(AudioHighPassFilter),
FilterKind.LowPass => typeof(AudioLowPassFilter),
FilterKind.Reverb => typeof(AudioReverbFilter),
_ => null,
};
}
}
使い方
//AudioListener 取得
var listener = Camera.main.GetComponent<AudioListener>();
//ユーティリティ生成
var effector = new ListenerEffector(listener);
//Reverb を追加してパラメータ上書き
effector.ApplyFilter<AudioReverbFilter>(f =>
{
f.reverbLevel = -1000f;
f.decayTime = 2.5f;
});
//フィルター無効化
effector.DisableFilter<AudioReverbFilter>();
//フィルター破棄
effector.RemoveFilter<AudioReverbFilter>();
実装のポイント
破棄と無効化の分離
-
DisableFilter:Behaviour.enabled = false(状態は保持する) -
RemoveFilter:Destroy(完全に削除し、GC 対象へ)
Discussion