Porta l'immersione del tuo gioco al livello successivo con una musica di gioco reattiva

La musica in grado di cambiare dinamicamente e senza interruzioni per riflettere su ciò che sta accadendo sullo schermo può aggiungere un nuovo livello di immersione a un gioco. In questo tutorial diamo uno sguardo a uno dei modi più semplici per aggiungere musica reattiva a un gioco.

Nota: Sebbene questo tutorial sia scritto utilizzando JavaScript e l'API Web Audio, dovresti essere in grado di utilizzare le stesse tecniche e concetti in quasi tutti gli ambienti di sviluppo di giochi.


dimostrazione

Ecco una demo di musica live responsive JavaScript con cui puoi giocare (con codice sorgente scaricabile). Puoi vedere una versione registrata della demo nel seguente video se il tuo browser non è in grado di eseguire la demo live:


Nota importante: Al momento della stesura di questo tutorial, l'API W3C Web Audio (utilizzata dalla demo JS) è una tecnologia sperimentale ed è disponibile solo nel browser Web Google Chrome.


introduzione

Journey, un gioco sviluppato da thatgamecompany, è un buon punto di partenza per questo tutorial. La grafica e la musica del gioco si fondono insieme per creare un'esperienza interattiva emozionante ed emozionante, ma c'è qualcosa di speciale nella musica del gioco che rende l'esperienza così potente - fluisce senza interruzioni nell'intero gioco, e si evolve dinamicamente come il giocatore progredisce e attiva determinati eventi di gioco. Journey usa la musica 'reattiva' per migliorare le emozioni che il giocatore prova durante il gioco.

Per essere onesti, molti giochi moderni usano la musica reattiva in un modo o nell'altro - Tomb Raider e Bioshock Infinite sono due esempi che ti vengono in mente - ma ogni gioco può beneficiare della musica reattiva.

Quindi, come puoi aggiungere musica reattiva ai tuoi giochi? Bene, ci sono molti modi per raggiungere questo obiettivo; alcuni modi sono molto più sofisticati di altri e richiedono che più canali audio vengano trasmessi da un dispositivo di archiviazione locale, ma l'aggiunta di una musica di base reattiva a un gioco è in realtà abbastanza semplice se si ha accesso a un'API audio di basso livello.

Daremo un'occhiata a una soluzione abbastanza semplice e abbastanza leggera da essere utilizzata oggi nei giochi online, compresi i giochi basati su JavaScript.


In poche parole

Il modo più semplice per ottenere musica reattiva in un gioco online è caricare un singolo file audio in memoria in fase di runtime e quindi eseguire il looping programmatico di sezioni specifiche di quel file audio. Ciò richiede uno sforzo coordinato da parte dei programmatori di giochi, ingegneri del suono e progettisti.

La prima cosa che dobbiamo considerare è la struttura attuale della musica.


Struttura musicale

La soluzione musicale reattiva che stiamo guardando qui richiede la musica deve essere strutturata in modo da consentire il loop continuo di parti della composizione musicale - queste parti loopable della musica saranno chiamate "zone" durante questo tutorial.

Oltre ad avere zone, la musica può sono costituiti da parti non loopabili che vengono utilizzate come transizioni tra varie zone - queste saranno chiamate "riempimenti" per tutto il resto di questo tutorial.

L'immagine seguente visualizza una struttura musicale molto semplice composta da due zone e due riempimenti:


Se sei un programmatore che ha utilizzato API audio di basso livello prima, potresti aver già capito dove stiamo andando: se la musica è strutturata in modo tale da consentire il loop di parti della composizione senza interruzioni, la musica può essere programmata in sequenza - tutto ciò che dobbiamo sapere è dove le zone e i riempimenti si trovano all'interno della musica. Ecco dove a descrittore il file è utile.

Nota: Non deve esserci alcun silenzio all'inizio della musica; deve iniziare immediatamente. Se all'inizio della musica vi è una porzione casuale di silenzio, le zone e i riempimenti della musica non saranno allineati alle barre (l'importanza di ciò sarà trattata più avanti in questo tutorial).


Descrittore musicale

Se vogliamo essere in grado di riprodurre e riprodurre in modo programmato parti specifiche di un file musicale, abbiamo bisogno di sapere dove si trovano le zone di musica e i riempimenti all'interno della musica. La soluzione più ovvia è un file descrittore che può essere caricato insieme alla musica, e per semplificare le cose useremo un file JSON perché la maggior parte dei linguaggi di programmazione sono in grado di decodificare e codificare i dati JSON in questi giorni.

Il seguente è un file JSON che descrive la struttura musicale semplice nell'immagine precedente:

 "bpm": 120, "bpb": 4, "struttura": ["tipo": 0, "dimensione": 2, "nome": "Rilassato", "tipo": 0, "dimensione" : 2, "nome": "Hunted", "type": 1, "size": 1, "name": "A", "type": 1, "size": 1, "name" : "B"]
  • Il bpm campo è il tempo della musica, in battiti al minuto.
  • Il BPB il campo è la firma della musica, in battute per battuta.
  • Il struttura il campo è una serie ordinata di oggetti che descrivono ciascuna zona e riempiono la musica.
  • Il genere il campo ci dice se l'oggetto è una zona o un riempimento (zero e uno rispettivamente).
  • Il taglia campo è la lunghezza o la zona o il riempimento, in barre.
  • Il nome il campo è un identificatore per la zona o il riempimento.

Tempismo musicale

Le informazioni nel descrittore musicale ci consentono di calcolare vari valori correlati al tempo necessari per riprodurre accuratamente la musica tramite un'API audio di basso livello.

Il bit più importante di informazioni di cui abbiamo bisogno è la lunghezza di una singola barra di musica, in campioni. Le zone musicali e i riempimenti sono tutti allineati alle barre e quando abbiamo bisogno di passare da una parte della musica all'altra la transizione deve avvenire all'inizio di una barra - non vogliamo che la musica salti da una posizione casuale all'interno di un bar perché suonerebbe davvero sconcertante.

Il seguente pseudocodice calcola la lunghezza del campione di una singola barra di musica:

 bpm = 120 // beats al minuto bpb = 4 // beats per bar srt = 44100 // sample rate bar_length = srt * (60 / (bpm / bpb))

Con il bar_length calcolato possiamo ora calcolare la posizione del campione e la lunghezza delle zone e riempirle all'interno della musica. Nel seguente pseudocodice semplicemente passiamo in rassegna il descrittore struttura array e aggiungere due nuovi valori alla zona e riempire gli oggetti:

 i = 0 n = descriptor.structure.length // numero di zone e riempimenti s = 0 while (i < n )  o = descriptor.structure[i++] o.start = s o.length = o.size * bar_length s += o.length 

Per questo tutorial, sono tutte le informazioni di cui abbiamo bisogno per la nostra soluzione musicale reattiva: ora conosciamo la posizione e la lunghezza del campione di ogni zona e riempiamo la musica, e questo significa che ora è possibile suonare le zone e riempire qualsiasi ordine ci piace. Essenzialmente, ora possiamo programmare una sequenza musicale infinitamente lunga in fase di esecuzione con un sovraccarico molto ridotto.


Riproduzione musicale

Ora che abbiamo tutte le informazioni necessarie per riprodurre la musica, suonare le zone e riempirle a livello di programmazione è un compito relativamente semplice, e possiamo gestirlo con due funzioni.

La prima funzione riguarda il compito di estrarre campioni dal nostro file musicale e portarli all'API del suono di basso livello. Ancora una volta, lo dimostrerò usando lo pseudocodice perché diversi linguaggi di programmazione hanno API diverse per fare questo genere di cose, ma la teoria è coerente in tutti i linguaggi di programmazione.

 input // buffer contenente i campioni dal nostro output musicale // audio di basso livello API output buffer playhead = 0 // posizione dell'headhead all'interno del file musicale, in sample start = 0 // start position della zona attiva o fill, in campioni lunghezza = 0 // lunghezza della zona attiva o riempimento, in campioni next = null // la zona successiva o riempimento (oggetto) che deve essere riprodotto // richiamato ogni volta che l'API audio di basso livello richiede più funzione di dati di esempio update () i = 0 n = output.length // lunghezza del campione del buffer di output 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 seconda funzione è utilizzata per accodare la zona o il riempimento successivo che deve essere riprodotto:

 // param 'name' è il nome della zona o del riempimento (definito nel descrittore) function setNext (name) i = 0 n = descriptor.structure.length // numero di zone e riempimenti mentre (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() 

Per suonare la zona della musica "rilassata", chiameremmo SetNext ( "Rilassati"), e la zona verrebbe messa in coda e quindi giocata alla prossima occasione possibile.

L'immagine seguente visualizza la riproduzione della zona 'Relaxed':


Per suonare la zona della musica "Hunted", chiameremmo SetNext ( "Hunted"):


Che ci crediate o meno, ora abbiamo abbastanza cose per aggiungere musica semplice e reattiva a qualsiasi gioco che abbia accesso a un'API audio di basso livello, ma non c'è alcun motivo per cui questa soluzione debba rimanere semplice: possiamo suonare varie parti di la musica in qualsiasi ordine ci piace, e questo apre le porte a colonne sonore più complesse.

Una delle cose che potremmo fare è raggruppare varie parti della musica per creare sequenze e quelle sequenze potrebbero essere utilizzate come transizioni complesse tra le diverse zone della musica.


Sequencing musicale

Il raggruppamento di varie parti della musica per creare sequenze sarà trattato in un futuro tutorial, ma nel frattempo consideriamo cosa sta succedendo nella seguente immagine:


Invece di passare direttamente da una sezione musicale molto rumorosa ad una sezione musicale molto silenziosa, potremmo tranquillizzare le cose gradualmente usando una sequenza - cioè, una transizione graduale.


Conclusione

Abbiamo esaminato una possibile soluzione per la musica di gioco reattiva in questo tutorial, utilizzando una struttura musicale e un descrittore musicale e il codice base richiesto per gestire la riproduzione musicale.

La musica reattiva può aggiungere un nuovo livello di immersione a un gioco ed è sicuramente qualcosa che gli sviluppatori di giochi dovrebbero considerare di sfruttare quando si inizia lo sviluppo di un nuovo gioco. Gli sviluppatori di giochi non dovrebbero commettere l'errore di lasciare questo tipo di cose fino agli ultimi stadi di sviluppo; richiede uno sforzo coordinato da parte dei programmatori di giochi, ingegneri del suono e designer.