Introducción al Desarrollo de Videojuegos con GameMaker · Módulo 9: Música, efectos sonoros y partículas.

Gestión de música de fondo y bucles (Loops)

Clase 42 de 60 · 50 min

También disponible en: EN

Introducción: La música de fondo en el diseño de videojuegos

La música de fondo (frecuentemente denominada BGM por sus siglas en inglés, Background Music) es uno de los componentes más influyentes en la experiencia del jugador. Mientras que los efectos sonoros puntuales (SFX) informan de acciones inmediatas como disparos, saltos o impactos, la música establece el tono emocional, la tensión dramática y el ritmo del nivel. En nuestro proyecto de plataformas retro 2D, una melodía animada y rítmica en los niveles transmite dinamismo y fluidez, mientras que una pieza más sosegada y ambiental en el menú principal prepara al usuario para iniciar la partida.

Desde una perspectiva puramente técnica de desarrollo de software, reproducir música presenta retos muy distintos a reproducir un efecto sonoro de medio segundo. Las pistas de música son archivos extensos, típicamente de uno a tres minutos de duración. Si no se configuran correctamente, pueden saturar la memoria RAM del sistema o generar interrupciones perceptibles al repetirse. Lograr que una pista musical se reproduzca en un ciclo infinito y perfecto (loop sin costuras), sin saltos de silencio ni cortes abruptos al cambiar de pantalla o reiniciar el nivel, requiere comprender la arquitectura del motor de audio de GameMaker y programar un sistema de gestión centralizado.

Configuración del recurso de sonido: Streaming frente a precarga

En GameMaker, tanto los efectos sonoros breves como las composiciones musicales se importan en el explorador de recursos dentro de la carpeta Sounds. No obstante, sus propiedades internas deben ajustarse de manera diametralmente opuesta. Cuando importamos un archivo de sonido (generalmente en formato .ogg o .mp3 para música), el editor de sonido nos permite configurar el modo de compresión y el tipo de carga en memoria.

Para los efectos de sonido, lo habitual es descomprimir el audio completo en la memoria RAM al iniciar el juego o al cargar la sala, lo que garantiza una latencia cero al disparar el sonido. Si hiciéramos esto con una pista de música estéreo de tres minutos sin comprimir, consumiría decenas de megabytes de memoria por cada pista. Por ello, para la música de fondo se utiliza la técnica de transmisión continua o lectura en flujo (Streaming). Con el streaming activado, GameMaker únicamente almacena un pequeño búfer temporal en la memoria y va decodificando el archivo comprimido desde el disco o almacenamiento del dispositivo a medida que avanza la reproducción.

ParámetroEfecto de Sonido (SFX)Música de Fondo (BGM)
Formato sugeridoWAV (sin compresión previa)OGG Vorbis (o MP3 con loop limpio)
Atributo TypeUncompressed / CompressedCompressed Not Streamed o Streamed
Modo de reproducciónDecodificado completo en RAMStreaming continuo desde disco
Canales de audioMono (habitual en 2D) o EstéreoEstéreo (para espacialidad y riqueza)
Consumo de recursosMayor uso de RAM, bajo uso de CPUMínimo uso de RAM, leve decodificación en CPU

Para configurar tu pista de fondo en GameMaker, haz doble clic sobre el recurso de sonido (por ejemplo, snd_musica_nivel1). En la ventana del Inspector o Editor de Sonido, localiza el desplegable 'Type' y selecciona 'Compressed - Streamed'. Ajusta la calidad de compresión a una tasa de bits de entre 128 kbps y 192 kbps, lo que ofrece un balance idóneo entre fidelidad acústica y peso de archivo para un juego de plataformas 2D.

Reproducción en bucle con audio_play_sound

La función fundamental para iniciar cualquier reproducción de audio en GML Code es audio_play_sound(). En versiones modernas de GameMaker, esta función acepta tanto parámetros posicionales básicos como argumentos extendidos para ganancia y desplazamiento temporal. Su signatura tradicional y más utilizada para principiantes es la siguiente:

audio_play_sound(index, priority, loop, [gain], [offset], [pitch], [listener_mask]);

Analicemos cada uno de los parámetros esenciales que intervienen en la música de fondo: 1. index: Es el identificador del recurso de audio que creamos en el Asset Browser, como snd_musica_menu o snd_musica_nivel1. 2. priority: Un valor numérico entero (generalmente entre 0 y 100). Si el juego llega al límite de canales de voz simultáneos configurados en el hardware, GameMaker detendrá los sonidos con menor prioridad para favorecer a los de mayor prioridad. La música de fondo debe tener una prioridad muy alta (por ejemplo, 10 o 50), mientras que un sonido de pasos o monedas suele tener prioridad baja (1 o 2). 3. loop: Un valor booleano (true o false). Si pasamos true, la pista se reiniciará inmediatamente al llegar al final de su duración, repitiéndose de manera indefinida hasta que el código indique expresamente su detención.

Es crucial entender lo que devuelve esta función: audio_play_sound no devuelve un valor booleano de éxito, sino un Sound Instance ID (identificador único de la instancia de sonido en reproducción). GameMaker distingue entre el Sound Asset (el molde original en el Asset Browser) y la Sound Instance (la reproducción viva en los canales de audio). Guardar este identificador en una variable nos permitirá alterar el volumen de esa pista específica, pausarla o detenerla más adelante sin interferir con otros sonidos del juego.

Diagrama conceptual que ilustra la arquitectura de gestión de música en GameMaker: el objeto controlador persistente administra la reproducción continua y las transiciones de pista entre niveles.
Figura 1: Flujo de control de música continuo mediante un objeto controlador persistente. · Imagen generada con IA

El problema de la duplicación y el objeto controlador de audio

Un error muy frecuente al iniciarse en el desarrollo de videojuegos consiste en colocar la llamada audio_play_sound en el evento Create del objeto del jugador (obj_player) o en el código de creación de la sala (Room Creation Code). Si colocamos la música en el jugador, cada vez que este pierda una vida y la sala se reinicie con room_restart(), se creará una nueva instancia del jugador que volverá a disparar la función. El resultado será una cacofonía insoportable donde dos o más copias desfasadas de la misma canción sonarán a la vez.

La solución arquitectónica profesional es centralizar la gestión sonora en un único objeto dedicado: obj_audio_controller. Este objeto se marca como Persistente (marcando la casilla 'Persistent' en sus propiedades de objeto). Los objetos persistentes sobreviven a los cambios de sala; una vez que entran en existencia en la primera sala del juego, continúan ejecutándose de fondo a lo largo de todos los niveles sin destruirse ni reiniciarse.

Implementación técnica de obj_audio_controller

A continuación, implementaremos un sistema completo en GML Code. Crearemos el objeto obj_audio_controller, lo marcaremos como persistente y le asignaremos dos eventos: el evento Create (donde inicializaremos las variables de seguimiento) y el evento Room Start (que se ejecuta automáticamente cada vez que el juego entra a una nueva sala).

// ========================================================
// OBJETO: obj_audio_controller
// EVENTO: Create
// ========================================================

// Variable para almacenar la instancia de sonido de la música activa
musica_actual_instancia = -1;

// Variable para rastrear qué recurso (asset) está sonando
musica_actual_asset = noone;

// Volumen base predeterminado de la música (rango de 0.0 a 1.0)
volumen_musica = 0.8;

En el evento Create definimos musica_actual_instancia como -1 (indicando que no hay ninguna instancia activa) y musica_actual_asset como noone (palabra clave de GML que indica la ausencia deliberada de un recurso o instancia). Ahora utilizaremos el evento Room Start para determinar dinámicamente qué pista debe sonar en función de la sala actual, evitando reiniciar la melodía si la sala de destino comparte la misma pista.

// ========================================================
// OBJETO: obj_audio_controller
// EVENTO: Other -> Room Start
// ========================================================

// Variable local para determinar qué canción corresponde a esta sala
var _musica_a_reproducir = noone;

// Evaluamos en qué sala nos encontramos actualmente
switch (room)
{
    case rm_menu:
        _musica_a_reproducir = snd_musica_menu;
        break;
        
    case rm_nivel_1:
    case rm_nivel_2:
        // Ambas salas de juego pueden compartir la misma música de acción
        _musica_a_reproducir = snd_musica_nivel1;
        break;
        
    case rm_game_over:
        _musica_a_reproducir = snd_musica_derrota;
        break;
        
    default:
        _musica_a_reproducir = noone;
        break;
}

// Comprobamos si la música requerida es diferente a la que ya está sonando
if (_musica_a_reproducir != musica_actual_asset)
{
    // 1. Si ya había una música sonando, la detenemos
    if (audio_is_playing(musica_actual_instancia))
    {
        audio_stop_sound(musica_actual_instancia);
    }
    
    // 2. Si la nueva sala requiere música, la iniciamos en bucle continuo
    if (_musica_a_reproducir != noone)
    {
        // Prioridad 10 para garantizar permanencia, loop = true
        musica_actual_instancia = audio_play_sound(_musica_a_reproducir, 10, true);
        
        // Asignamos el volumen deseado a la instancia recién creada
        audio_sound_gain(musica_actual_instancia, volumen_musica, 0);
        
        // Actualizamos el registro del asset en reproducción
        musica_actual_asset = _musica_a_reproducir;
    }
    else
    {
        // Si la sala no tiene música asignada, limpiamos las variables
        musica_actual_instancia = -1;
        musica_actual_asset = noone;
    }
}

Observa con atención la condición lógica `if (_musica_a_reproducir != musica_actual_asset)`. Si el jugador pasa de `rm_nivel_1` a `rm_nivel_2`, ambas salas solicitan `snd_musica_nivel1`. Dado que el recurso solicitado coincide con el que ya se está ejecutando, el bloque condicional no interrumpe la reproducción. La música continúa sonando sin ningún corte ni reinicio de tempo a través del cambio de pantalla, ofreciendo una experiencia inmersiva y profesional.

Transiciones suaves: Fundido de volumen (Fade In y Fade Out)

Detener una pista musical de forma instantánea mediante audio_stop_sound() produce un corte seco que resulta artificial al oído humano. En producciones de videojuegos comerciales, los cambios de melodía se realizan mediante desvanecimientos progresivos conocidos como Fade Out (reducción gradual del volumen hasta el silencio) y Fade In (incremento gradual del volumen desde cero hasta el nivel objetivo).

GameMaker incluye una función extraordinariamente potente para gestionar esta interpolación de forma automática sin necesidad de programar temporizadores manuales en el evento Step: audio_sound_gain().

audio_sound_gain(index, volume, time);

El parámetro `time` se especifica en milisegundos (1000 milisegundos equivalen a 1 segundo). Si llamamos a `audio_sound_gain(musica_actual_instancia, 0, 1500)`, el motor de audio reducirá el volumen de forma lineal durante segundo y medio. Cuando el volumen llega a 0, podemos detener definitivamente la pista para liberar la voz.

Control de pausa y reanudación de la música

En el Módulo 8 desarrollamos el sistema de pausa interactiva. Durante la pausa de un juego, existen dos opciones estilísticas de diseño sonoro: 1. Mantener la música de fondo sonando al mismo volumen o a un volumen atenuado (Duck de volumen). 2. Pausar la música exactamente en la posición temporal donde se encontraba y reanudarla al quitar la pausa.

GameMaker ofrece las funciones dedicadas audio_pause_sound() y audio_resume_sound(). A diferencia de audio_stop_sound() (que reinicia la aguja de reproducción al segundo 0), audio_pause_sound() congela la posición del cabezal de lectura. Al invocar audio_resume_sound(), la melodía prosigue sin perder el ritmo donde el jugador se quedó.

// Al activar la pausa en obj_pause_manager:
if (audio_is_playing(obj_audio_controller.musica_actual_instancia))
{
    // Opción A: Pausar en seco
    // audio_pause_sound(obj_audio_controller.musica_actual_instancia);
    
    // Opción B: Atenuación de volumen (Duck) al 30% para menú de pausa
    audio_sound_gain(obj_audio_controller.musica_actual_instancia, 0.25, 300);
}

// Al reanudar la partida:
if (audio_is_playing(obj_audio_controller.musica_actual_instancia))
{
    // Restaurar el volumen original del controlador en 300 milisegundos
    audio_sound_gain(obj_audio_controller.musica_actual_instancia, obj_audio_controller.volumen_musica, 300);
}

Perspectiva en GML Visual

Para aquellos estudiantes que utilicen GML Visual de forma paralela, GameMaker cuenta con bloques de acción equivalentes en la categoría Audio. La acción 'Play Audio' permite seleccionar el recurso de sonido, asignar el campo 'Loop' en True y definir el volumen inicial. Para el control de volumen temporal, la acción 'Set Audio Volume' permite especificar el valor objetivo y la duración en milisegundos del desvanecimiento. No obstante, en proyectos de complejidad media como nuestro plataformas, la gestión de condiciones mediante GML Code ofrece una precisión y limpieza de arquitectura significativamente superiores.

💡 Buenas prácticas de edición: Asegúrate de que el archivo de audio que importes no tenga silencios grabados al principio o al final de la onda. Si un archivo .ogg contiene 0.2 segundos de silencio al final antes de reiniciar, el bucle sonará con un corte rítmico desagradable. Edita tus bucles en un software de edición de audio (como Audacity) asegurándote de que los puntos de corte coincidan con cruces por cero (zero-crossing).

Ejercicios prácticos

Aplica los conceptos aprendidos completando los tres ejercicios siguientes en tu proyecto de plataformas:

Ejercicio 1: Importación y configuración de audio en streaming. Importa al menos dos pistas de música distintas en tu proyecto: una para el menú principal (snd_musica_menu) y otra para el nivel de acción (snd_musica_nivel1). Configura ambas en el Inspector con compresión 'Compressed - Streamed'. Comprueba que el formato de archivo sea compatible (.ogg recomendado) y que al reproducirse en bucle dentro de la ventana de vista previa no existan silencios muertos.

Ejercicio 2: Creación del controlador persistente. Crea el objeto obj_audio_controller y marca su casilla 'Persistent'. Colócalo una única vez en tu primera sala del juego (por ejemplo, rm_menu o una sala de inicio rm_init). Escribe el código del evento Create y Room Start mostrado en esta lección. Comprueba que al pasar del menú al nivel 1 la música cambie correctamente, y que si el jugador muere y se recarga el nivel 1 mediante room_restart(), la pista no se duplique ni se superponga.

Ejercicio 3: Transición dinámica por Game Over con fundido. Implementa una tercera pista para la sala rm_game_over (snd_musica_derrota). En lugar de cortar la música del nivel de manera inmediata al morir, haz que el objeto de control aplique un fundido de salida de 1000 milisegundos antes de cambiar de sala hacia rm_game_over. Comprueba con el oído que la transición sea fluida y que no se produzcan chasquidos sonoros (clicks/pops).

Resumen

  • Las pistas de música de fondo deben configurarse como 'Compressed - Streamed' para evitar consumos masivos de memoria RAM y permitir una decodificación eficiente sobre la marcha.
  • La función audio_play_sound(index, priority, loop) con el argumento loop establecido en true garantiza una reproducción infinita y devuelve un Sound Instance ID único para gestionar esa voz específica.
  • Nunca se debe reproducir música en bucle desde objetos volátiles como el jugador o dentro del código directo de la sala; debe gestionarse a través de un objeto controlador persistente (obj_audio_controller).
  • El evento Room Start permite evaluar con switch(room) qué música corresponde a la pantalla activa, verificando si ya está en reproducción para no interrumpir el flujo musical entre niveles compartidos.
  • La función audio_sound_gain(instance_id, volume, time_ms) automatiza transiciones de desvanecimiento suave (fade in y fade out), evitando cortes secos y elevando la calidad profesional del videojuego.

Practica lo aprendido

Ejercicio 1 · 1 punto

Al configurar un recurso de música de fondo (BGM) en el editor de sonido de GameMaker para optimizar el uso de memoria RAM, ¿cuál es la opción técnica adecuada en el desplegable 'Type'?

Practicas sin cuenta: no se guarda nada. Regístrate para guardar tu progreso.

Ejercicio 2 · 1 punto

La función audio_play_sound() devuelve un valor booleano (true o false) que indica si el archivo de audio se pudo reproducir con éxito.

Practicas sin cuenta: no se guarda nada. Regístrate para guardar tu progreso.

Ejercicio 3 · 2 puntos

Completa la llamada a la función para reproducir la pista de audio snd_musica_nivel1 en bucle continuo infinito con una prioridad de 10, y guarda el Sound Instance ID retornado en la variable musica_actual_instancia.

Tu código no se ejecuta: se comprueban requisitos concretos sobre el texto.

Practicas sin cuenta: no se guarda nada. Regístrate para guardar tu progreso.

Ingresa para guardar tu progreso — Ingresar