Quick answer: Look up bus and effect indices once at startup and cache them. Use AudioServer.get_bus_index("BusName") then iterate effects to find by class. Toggle via cached indices.
You toggle a LowPass effect for underwater audio with AudioServer.set_bus_effect_enabled(2, 0, true). Doesn’t change audio. The hardcoded indices (2, 0) don’t match the runtime bus layout.
Index Lookup Helper
var _lowpass_bus_idx: int = -1
var _lowpass_effect_idx: int = -1
func _ready():
_lowpass_bus_idx = AudioServer.get_bus_index("SFX")
if _lowpass_bus_idx == -1:
push_error("SFX bus not found")
return
var count = AudioServer.get_bus_effect_count(_lowpass_bus_idx)
for i in count:
var e = AudioServer.get_bus_effect(_lowpass_bus_idx, i)
if e is AudioEffectLowPassFilter:
_lowpass_effect_idx = i
break
func enter_water():
AudioServer.set_bus_effect_enabled(_lowpass_bus_idx, _lowpass_effect_idx, true)
func exit_water():
AudioServer.set_bus_effect_enabled(_lowpass_bus_idx, _lowpass_effect_idx, false)
Lookup once, cache, use by index. Rearranging the bus layout doesn’t break the code; the lookup runs again on init.
Diagnose Index Issues
for b in AudioServer.get_bus_count():
print(b, ": ", AudioServer.get_bus_name(b))
for e in AudioServer.get_bus_effect_count(b):
print(" ", e, ": ", AudioServer.get_bus_effect(b, e).get_class())
Prints the full bus + effect layout. Verifies your assumed indices match reality.
Editor vs Runtime Layout
If you saved a default_bus_layout.tres but a different layout is loaded at runtime (via Project Settings → Audio → Bus Layout), indices differ. Stick to one layout; check Project Settings to verify which is active.
Verifying
Toggle enter_water() / exit_water() and listen. Low-pass filter applies/removes. The cached indices remain stable across runs of the same project.
“Index-based audio API is fast but fragile. Cache by lookup at startup; never hardcode indices.”
Build an AudioBus singleton with named-effect helpers — gameplay code calls AudioBus.enable_underwater() instead of remembering indices.