Parte: 6 — Audio y música interactiva · Fuente: Godot Docs — Importing audio samples + referencias de AudioStreamWAV, AudioStreamOggVorbis y AudioStreamPlayer ⏱️ Duración estimada: 50 min · Nivel: Intermedio
Aprender a que el audio no se coma la memoria ni el CPU: elegir entre WAV y OGG con criterio, activar streaming para la música, limitar el número de voces simultáneas y reutilizar reproductores con un pool. Al terminar habrás construido un pool de AudioStreamPlayer para SFX con límite de voces que puedes medir, y habrás configurado la música para que se transmita desde disco en lugar de cargarse entera en RAM.
Al finalizar, el alumno podrá:
| # | Tema | Por qué importa |
|---|---|---|
| 1 | WAV vs OGG | El formato define cuánta RAM y cuánto CPU cuesta cada sonido |
| 2 | Streaming de música | Evita cargar canciones enteras en memoria |
| 3 | Límite de voces (polifonía) | Demasiadas voces saturan el CPU y ensucian la mezcla |
| 4 | Presupuesto de audio | Fijar un tope de voces y memoria guía todas las decisiones |
| 5 | Pool de reproductores | Reutilizar nodos evita crear/destruir en caliente |
| 6 | Distancia de corte | No reproducir lo que el jugador no oiría ahorra voces |
| 7 | Robo de voz (voice stealing) | Qué hacer cuando el pool está lleno |
| 8 | Medir para decidir | Sin datos, la "optimización" es adivinar |
AudioStreamPlayer reutilizables. Clave: cero asignaciones en runtime y control total del máximo de voces.Necesitas Godot 4.x, un puñado de SFX cortos (impactos, pasos) que importarás como WAV, y una canción larga que importarás como OGG con streaming activado. En el inspector de importación de Godot puedes marcar Loop y, para el OGG, comprobar que el recurso es un AudioStreamOggVorbis con streaming. Revisa la guía de importar samples de audio, la referencia de AudioStreamWAV y la de AudioStreamOggVorbis. Abre el Monitor del depurador (pestaña Debugger → Monitors) para observar memoria y número de nodos mientras pruebas el pool.
Construiremos un pool de reproductores para SFX con un tope de voces y robo del más viejo, y configuraremos la música con streaming. Contaremos las voces activas para medir el efecto del límite.
Paso 1 — Escena. Crea una escena con un Node raíz (SfxPool). Adjunta un script. La música vivirá en un AudioStreamPlayer aparte con el OGG en streaming asignado en stream.
Paso 2 — Crear el pool. En _ready, instancia N reproductores hijos de una sola vez. Ese N es tu presupuesto de voces para SFX:
extends Node
@export var max_voces: int = 8 # presupuesto de polifonía para SFX
@export var bus_sfx: String = "SFX"
var _pool: Array[AudioStreamPlayer] = []
var _voces_activas: int = 0
func _ready() -> void:
for i in max_voces:
var p := AudioStreamPlayer.new()
p.bus = bus_sfx
add_child(p)
# Al terminar, la voz vuelve a estar libre.
p.finished.connect(_on_voz_libre)
_pool.append(p)
func _on_voz_libre() -> void:
_voces_activas = maxi(_voces_activas - 1, 0)
Paso 3 — Reproducir con reutilización. Busca un reproductor libre; si no hay, roba el que lleva más tiempo sonando. Nunca creas nodos nuevos:
func reproducir(sfx: AudioStream, volumen_db: float = 0.0, pitch: float = 1.0) -> void:
var libre := _buscar_libre()
if libre == null:
libre = _robar_mas_viejo() # pool lleno: voice stealing
else:
_voces_activas += 1
libre.stream = sfx
libre.volume_db = volumen_db
libre.pitch_scale = pitch
libre.play()
func _buscar_libre() -> AudioStreamPlayer:
for p in _pool:
if not p.playing:
return p
return null
func _robar_mas_viejo() -> AudioStreamPlayer:
# Estrategia simple: el primero de la lista. Reordenamos para rotar.
var victima: AudioStreamPlayer = _pool.pop_front()
_pool.push_back(victima)
victima.stop()
return victima
Paso 4 — Distancia de corte. Antes de gastar una voz, descarta lo que el jugador no oiría. Pásale la posición del emisor y del oyente:
func reproducir_posicional(sfx: AudioStream, origen: Vector2, oyente: Vector2, corte: float = 800.0) -> void:
if origen.distance_to(oyente) > corte:
return # inaudible: no gastamos voz
# Atenúa por distancia de forma simple (más lejos, más bajo).
var d := origen.distance_to(oyente)
var vol := lerpf(0.0, -40.0, clampf(d / corte, 0.0, 1.0))
reproducir(sfx, vol)
Paso 5 — Medir. Expón el número de voces activas para verlo mientras disparas SFX en ráfaga:
func _process(_delta: float) -> void:
# En un juego real, dibújalo en un Label de depuración.
# Aquí basta con imprimir cada cierto tiempo o al pulsar una tecla.
pass
func voces_activas() -> int:
return _voces_activas
Paso 6 — Música con streaming. Verifica en el inspector que el OGG de la música tiene streaming (es el comportamiento de AudioStreamOggVorbis) y arráncala aparte del pool:
# En el nodo de música (otro AudioStreamPlayer):
# $Musica.stream es un AudioStreamOggVorbis (se transmite desde disco).
# $Musica.play() -> memoria casi constante aunque la canción dure minutos.
Paso 7 — Probar. Dispara 20 SFX en un instante: verás que nunca suenan más de max_voces a la vez porque el pool roba las voces más viejas, y que la memoria se mantiene estable gracias al streaming de la música. Sube y baja max_voces y observa el cambio en densidad sonora y en el monitor.
Resultado visible: una ráfaga de SFX limitada a N voces simultáneas, con memoria estable y voces robadas de forma controlada.
Label las voces activas y el tamaño del pool en tiempo real.AudioStreamPlayer2D con atenuación real por distancia.pitch_scale aleatoria para que los SFX repetidos no cansen.Entrega un sistema de audio con un pool de SFX de tamaño configurable (con robo de voz y distancia de corte) y música en streaming, más un panel de depuración que muestre las voces activas. Demuestra que, ante una ráfaga de peticiones, nunca se superan las voces del presupuesto.
Criterio de aceptación: al lanzar más SFX que max_voces en un frame, el contador de voces activas nunca excede max_voces; la música larga se reproduce sin que la memoria crezca de forma sostenida; y los sonidos fuera de la distancia de corte no consumen voces.
| Síntoma | Causa y arreglo |
|---|---|
| La memoria se dispara con la música | La importaste como WAV o sin streaming; usa OGG con streaming para pistas largas |
| Chasquidos y CPU alto con muchos SFX | Sin límite de voces; introduce un pool con tope y robo del más viejo |
Crear un AudioStreamPlayer por cada disparo tironea |
Asignar/liberar nodos en caliente es caro; reutiliza un pool fijo |
| Suenan sonidos que deberían ser inaudibles | No aplicas distancia de corte; descarta por distancia antes de reproducir |
| El contador de voces se desincroniza | No decrementas en finished; conecta la señal y ajusta el conteo al terminar |
¿Cuándo uso WAV y cuándo OGG? WAV para SFX cortos y muy repetidos (baja latencia, CPU casi nulo, más RAM). OGG para música y voces largas (poca RAM y disco, algo de CPU al descomprimir).
¿El streaming añade latencia a los SFX? Para SFX cortos no conviene: prefieres WAV descomprimido. El streaming es para pistas largas donde la memoria importa más que arrancar al instante.
¿Qué tamaño de pool elijo? Depende del presupuesto y del juego. Empieza pequeño (8-16 voces para SFX), mide en escenas densas y ajústalo hasta que no oigas cortes molestos.
¿Qué es "robar" una voz? Cuando el pool está lleno y llega un sonido nuevo, reemplazas el menos importante o el más viejo. Así respetas el tope sin perder los sonidos relevantes.
Clase 135 - Voces, diálogo y localización de audio