La música que es capaz de cambiar de forma dinámica y perfecta para reflejar lo que está sucediendo en la pantalla puede agregar un nuevo nivel de inmersión a un juego. En este tutorial, echamos un vistazo a una de las formas más fáciles de agregar música sensible a un juego..
Nota: Aunque este tutorial está escrito utilizando JavaScript y la API de audio web, debería poder usar las mismas técnicas y conceptos en casi cualquier entorno de desarrollo de juegos..
Esta es una demostración de música en vivo con capacidad de respuesta en JavaScript para que juegues (con código fuente descargable). Puede ver una versión grabada de la demostración en el siguiente video si su navegador web no puede ejecutar la demostración en vivo:
Nota IMPORTANTE: En el momento de escribir este tutorial, la API de audio de W3C Web (utilizada por la versión parcial de programa de JS) es una tecnología experimental y solo está disponible en el navegador web Google Chrome.
Journey, un juego desarrollado por thatgamecompany, es un buen punto de partida para este tutorial. Los gráficos y la música del juego se fusionan para crear una experiencia interactiva impactante y emocional, pero hay algo especial en la música del juego que hace que la experiencia sea tan poderosa como es: fluye sin problemas a través de todo el juego y evoluciona dinámicamente a medida que se desarrolla. el jugador avanza y dispara ciertos eventos en el juego. Journey utiliza música "sensible" para mejorar las emociones que el jugador experimenta mientras juega..
Para ser justos, muchos juegos modernos usan música sensible de una manera u otra: Tomb Raider y Bioshock Infinite son dos ejemplos que vienen a la mente, pero todos los juegos pueden beneficiarse de la música sensible..
Entonces, ¿cómo puedes agregar música sensible a tus juegos? Bueno, hay muchas maneras de lograr esto; algunas formas son mucho más sofisticadas que otras y requieren la transmisión de múltiples canales de audio desde un dispositivo de almacenamiento local, pero agregar música básica a un juego es bastante fácil si tiene acceso a una API de sonido de bajo nivel.
Vamos a echar un vistazo a una solución que es lo suficientemente simple y liviana para usarla hoy en día en los juegos en línea, incluidos los juegos basados en JavaScript..
La forma más fácil de lograr música sensible en un juego en línea es cargar un solo archivo de audio en la memoria en tiempo de ejecución, y luego hacer un bucle programático de secciones específicas de ese archivo de audio. Esto requiere un esfuerzo coordinado de los programadores de juegos, ingenieros de sonido y diseñadores..
Lo primero que debemos considerar es la estructura real de la música..
La solución de música sensible que estamos viendo aquí. requiere La música se estructurará de manera que permita que partes de la disposición musical se repitan sin problemas: estas partes de la música que se pueden conectar en bucle se llamarán "zonas" a lo largo de este tutorial..
Además de tener zonas, la música. puede consisten en partes no en bucle que se usan como transiciones entre varias zonas, se llamarán 'rellenos' durante el resto de este tutorial.
La siguiente imagen visualiza una estructura musical muy simple que consta de dos zonas y dos rellenos:
Si usted es un programador que ha usado API de sonido de bajo nivel anteriormente, es posible que ya haya averiguado a dónde nos dirigimos con esto: si la música está estructurada de tal manera que permita que partes del arreglo se puedan realizar de manera perfecta, el la música se puede secuenciar mediante programación; todo lo que necesitamos saber es dónde se encuentran las zonas y los rellenos dentro de la música. Ahí es donde un descriptor archivo viene en útil.
Nota: No debe haber ningún silencio al principio de la música; debe comenzar de inmediato. Si hay un fragmento aleatorio de silencio al principio de la música, las zonas y los rellenos de la música no se alinearán con las barras (la importancia de esto se tratará más adelante en este tutorial).
Si queremos poder reproducir y reproducir partes específicas de un archivo de música mediante programación, necesitamos saber dónde se encuentran las zonas y los rellenos de la música dentro de la música. La solución más obvia es un archivo descriptor que se puede cargar junto con la música, y para simplificar, vamos a utilizar un archivo JSON porque la mayoría de los lenguajes de programación son capaces de descodificar y codificar datos JSON en estos días..
El siguiente es un archivo JSON que describe la estructura de música simple en la imagen anterior:
"bpm": 120, "bpb": 4, "estructura": ["tipo": 0, "tamaño": 2, "nombre": "Relajado", "tipo": 0, "tamaño" : 2, "name": "Hunted", "type": 1, "size": 1, "name": "A", "type": 1, "size": 1, "name" : "B"]
bpm
El campo es el tempo de la música, en tiempos por minuto..bpb
Campo es la firma de la música, en tiempos por compás..estructura
campo es una matriz ordenada de objetos que describen cada zona y completan la música.tipo
el campo nos dice si el objeto es una zona o un relleno (cero y uno respectivamente).tamaño
campo es la longitud o la zona o relleno, en barras.nombre
campo es un identificador para la zona o relleno.La información en el descriptor de música nos permite calcular varios valores relacionados con el tiempo que se necesitan para reproducir con precisión la música a través de una API de sonido de bajo nivel.
El bit de información más importante que necesitamos es la longitud de una sola barra de música, en muestras. Las zonas y los rellenos musicales están todos alineados a compases, y cuando necesitamos hacer la transición de una parte de la música a otra, la transición debe ocurrir al comienzo de un compás: no queremos que la música salte de una posición aleatoria. dentro de un bar porque sonaría realmente desconcertante.
El siguiente pseudocódigo calcula la longitud de muestra de una sola barra de música:
bpm = 120 // tiempos por minuto bpb = 4 // tiempos por compás srt = 44100 // frecuencia de muestreo bar_length = srt * (60 / (bpm / bpb))
Con el bar_length
calculado ahora podemos calcular la posición de la muestra y la longitud de las zonas y los rellenos dentro de la música. En el siguiente pseudocódigo simplemente hacemos un bucle a través del descriptor estructura
Arregle y agregue dos nuevos valores a la zona y rellene los objetos:
i = 0 n = descriptor.structure.length // número de zonas y rellena s = 0 mientras (i < n ) o = descriptor.structure[i++] o.start = s o.length = o.size * bar_length s += o.length
Para este tutorial, esa es toda la información que necesitamos para nuestra solución de música sensible: ahora conocemos la posición de la muestra y la longitud de cada zona y completamos la música, y eso significa que ahora pueden reproducir las zonas y rellenos en cualquier orden nos gusta. Esencialmente, ahora podemos secuenciar programáticamente una pista de música infinitamente larga en tiempo de ejecución con muy poca sobrecarga.
Ahora que tenemos toda la información que necesitamos para reproducir música, la reproducción programada de zonas y los rellenos de la música es una tarea relativamente simple, y podemos manejar esto con dos funciones.
La primera función trata con la tarea de extraer muestras de nuestro archivo de música y enviarlas a la API de sonido de bajo nivel. Nuevamente, demostraré esto usando pseudocódigo porque diferentes lenguajes de programación tienen diferentes API para hacer este tipo de cosas, pero la teoría es consistente en todos los lenguajes de programación.
entrada // búfer que contiene las muestras de nuestra salida de música // bajo nivel de sonido API salida búfer cabeza de reproducción = 0 // posición de la cabeza de reproducción dentro del archivo de música, en muestras inicio = 0 // posición de inicio de la zona activa o relleno, en muestras length = 0 // longitud de la zona activa o relleno, en samples siguiente = null // la siguiente zona o relleno (objeto) que debe reproducirse // invocado cada vez que la API de sonido de bajo nivel requiera más funciones de datos de muestra update () i = 0 n = output.length // longitud de la muestra del búfer de salida end = length - start while (i < n ) // is the playhead at the end of the active zone or fill if( playhead == end ) // is another zone or fill waiting to be played if( next != null ) start = next.start length = next.length next = null // reset the playhead playhead = start // pull samples from the input and push them to the output output[i++] = input[playhead++]
La segunda función se utiliza para poner en cola la siguiente zona o el relleno que se debe reproducir:
// param 'nombre' es el nombre de la zona o función de relleno (definida en el descriptor) setNext (nombre) i = 0 n = descriptor.structure.length // número de zonas y rellenos mientras (i < n ) o = descriptor.structure[i++] if( o.name == name ) // set the 'next' value and return from the function next = o return // the requested zone or fill could not be found throw new Exception()
Para tocar la zona de música 'relajada', llamaríamos setNext ("relajado")
, y la zona se pondría en cola y luego se jugaría en la próxima oportunidad posible.
La siguiente imagen visualiza la reproducción de la zona 'Relajada':
Para tocar la zona de la música 'Hunted', llamaríamos setNext ("Hunted")
:
Créalo o no, ahora tenemos suficiente para trabajar con música de respuesta simple para cualquier juego que tenga acceso a una API de sonido de bajo nivel, pero no hay ninguna razón por la que esta solución deba seguir siendo simple: podemos jugar varias partes de La música en el orden que queramos, y eso abre la puerta a bandas sonoras más complejas..
Una de las cosas que podríamos hacer es agrupar varias partes de la música para crear secuencias, y esas secuencias podrían usarse como transiciones complejas entre las diferentes zonas de la música..
Agrupar varias partes de la música para crear secuencias se tratará en un futuro tutorial, pero mientras tanto, considere lo que está sucediendo en la siguiente imagen:
En lugar de pasar directamente de una sección de música muy alta a una sección de música muy tranquila, podríamos calmar las cosas gradualmente usando una secuencia, es decir, una transición suave.
Hemos analizado una posible solución para la música de juego sensible en este tutorial, utilizando una estructura de música y un descriptor de música, y el código principal requerido para manejar la reproducción de música.
La música receptiva puede agregar un nuevo nivel de inmersión a un juego y es definitivamente algo que los desarrolladores de juegos deberían considerar aprovechar al iniciar el desarrollo de un nuevo juego. Sin embargo, los desarrolladores de juegos no deben cometer el error de dejar este tipo de cosas hasta las últimas etapas de desarrollo; requiere un esfuerzo coordinado de los programadores de juegos, ingenieros de sonido y diseñadores.