Musik, die sich dynamisch und nahtlos ändern kann, um auf dem Bildschirm darzustellen, was auf dem Bildschirm passiert, kann einem Spiel eine völlig neue Ebene des Eintauchens geben. In diesem Tutorial betrachten wir eine der einfacheren Möglichkeiten, einem Spiel responsive Musik hinzuzufügen.
Hinweis: Obwohl dieses Tutorial mit JavaScript und der Web-Audio-API geschrieben wurde, sollten Sie in der Lage sein, in nahezu jeder Spieleentwicklungsumgebung dieselben Techniken und Konzepte zu verwenden.
Hier ist eine live responsive Musik-Demo von JavaScript zum Herunterladen (mit herunterladbarem Quellcode). Sie können eine aufgezeichnete Version der Demo im folgenden Video ansehen, wenn Ihr Webbrowser die Live-Demo nicht ausführen kann:
Wichtige Notiz: Zum Zeitpunkt der Erstellung dieses Tutorials ist die W3C Web Audio API (von der JS-Demo verwendet) eine experimentelle Technologie, die nur im Google Chrome-Webbrowser verfügbar ist.
Journey, ein von thatgamecompany entwickeltes Spiel, ist ein guter Ausgangspunkt für dieses Tutorial. Die Grafik und die Musik des Spiels verschmelzen zu einem atemberaubenden und emotionalen interaktiven Erlebnis. Die Musik im Spiel hat jedoch etwas Besonderes, das das Erlebnis so mächtig macht, wie es ist - es fließt nahtlos durch das gesamte Spiel und entwickelt sich dynamisch Der Spieler führt bestimmte Ereignisse im Spiel aus und löst diese aus. Journey verwendet "responsive" Musik, um die Emotionen zu verbessern, die der Spieler während des Spiels erlebt.
Um ehrlich zu sein, verwenden viele moderne Spiele responsive Musik auf die eine oder andere Weise - Tomb Raider und Bioshock Infinite sind zwei Beispiele, die mir in den Sinn kommen - aber jedes Spiel kann von responsiver Musik profitieren.
Wie können Sie Ihren Spielen also reaktionsfähige Musik hinzufügen? Nun, es gibt zahlreiche Möglichkeiten, dies zu erreichen. Einige Möglichkeiten sind komplexer als andere und erfordern, dass mehrere Audiokanäle von einem lokalen Speichergerät gestreamt werden. Das Hinzufügen von grundlegender responsiver Musik zu einem Spiel ist jedoch ziemlich einfach, wenn Sie Zugriff auf eine Low-Level-Sound-API haben.
Wir werden einen Blick auf eine Lösung werfen, die einfach und leicht genug ist, um sie heute in Online-Spielen zu verwenden - einschließlich JavaScript-basierter Spiele.
Der einfachste Weg, reaktionsfähige Musik in einem Online-Spiel zu erzielen, besteht darin, zur Laufzeit eine einzelne Audiodatei in den Speicher zu laden und dann bestimmte Abschnitte dieser Audiodatei programmgesteuert zu schleifen. Dies erfordert einen koordinierten Aufwand von Spielprogrammierern, Toningenieuren und Designern.
Das erste, was wir beachten müssen, ist die tatsächliche Struktur der Musik.
Die responsive Musiklösung, die wir hier betrachten erfordert Die Musik muss so strukturiert sein, dass Teile des musikalischen Arrangements nahtlos durchlaufen werden können. Diese schleifenfähigen Teile der Musik werden in diesem Tutorial als Zonen bezeichnet.
Neben den Zonen, der Musik können Sie bestehen aus nicht schleifenfähigen Teilen, die als Übergänge zwischen verschiedenen Zonen verwendet werden. Diese werden im weiteren Verlauf dieses Tutorials als "Füllungen" bezeichnet.
Das folgende Bild visualisiert eine sehr einfache Musikstruktur, die aus zwei Zonen und zwei Füllungen besteht:
Wenn Sie ein Programmierer sind, der bereits Low-Level-Sound-APIs verwendet hat, haben Sie möglicherweise bereits herausgefunden, wohin wir damit gehen sollen: Wenn die Musik so strukturiert ist, dass Teile des Arrangements nahtlos geloopt werden können, wird die Musik kann programmgesteuert sequenziert werden - wir müssen nur wissen, wo sich die Zonen und Füllungen in der Musik befinden. Das ist, wo ein Deskriptor Datei kommt nützlich.
Hinweis: Es darf keine Stille zu Beginn der Musik geben; es muss sofort beginnen. Wenn zu Beginn der Musik ein zufälliges Stück Stille herrscht, werden die Zonen und Füllungen der Musik nicht an Bars ausgerichtet (die Bedeutung dieser Funktion wird später in diesem Lernprogramm behandelt)..
Wenn Sie bestimmte Teile einer Musikdatei programmgesteuert abspielen und schleifen möchten, müssen wir wissen, wo sich die Musikzonen und Füllungen in der Musik befinden. Die naheliegendste Lösung ist eine Deskriptordatei, die zusammen mit der Musik geladen werden kann. Zur Vereinfachung verwenden wir eine JSON-Datei, da die meisten Programmiersprachen heutzutage JSON-Daten decodieren und codieren können.
Im Folgenden finden Sie eine JSON-Datei, die die einfache Musikstruktur im vorherigen Bild beschreibt:
"bpm": 120, "bpb": 4, "structure": ["type": 0, "size": 2, "name": "Relaxed", "type": 0, "size" : 2, "name": "Hunted", "type": 1, "size": 1, "name": "A", "type": 1, "size": 1, "name" : "B"]
bpm
field ist das Tempo der Musik in Beats pro Minute.bpb
Feld ist die Signatur der Musik in Beats pro Takt.Struktur
Feld ist ein geordnetes Array von Objekten, die jede Zone beschreiben und die Musik füllen.Art
Feld gibt an, ob das Objekt eine Zone oder eine Füllung ist (Null bzw. Eins).Größe
Feld ist die Länge oder die Zone oder Füllung in Balken.Name
Feld ist ein Bezeichner für die Zone oder Füllung.Die Informationen in der Musikbeschreibung ermöglichen es uns, verschiedene zeitbezogene Werte zu berechnen, die erforderlich sind, um die Musik über eine Low-Level-Sound-API genau abzuspielen.
Die wichtigste Information, die wir brauchen, ist die Länge eines einzelnen Taktes in Samples. Die musikalischen Zonen und Fills sind alle an Takten ausgerichtet, und wenn wir von einem Teil der Musik zu einem anderen wechseln müssen, muss der Übergang zu Beginn einer Bar erfolgen - wir möchten nicht, dass die Musik aus einer zufälligen Position springt innerhalb einer Bar, weil es wirklich beunruhigend klingen würde.
Der folgende Pseudocode berechnet die Sample-Länge eines einzelnen Musikbalkens:
bpm = 120 // Schläge pro Minute bpb = 4 // Schläge pro Takt srt = 44100 // Abtastrate bar_length = srt * (60 / (bpm / bpb))
Mit dem bar_length
Berechnet können wir nun die Sampleposition und Länge der Zonen und Füllungen innerhalb der Musik ermitteln. Im folgenden Pseudocode durchlaufen wir einfach den Deskriptor Struktur
fügen Sie der Zone zwei neue Werte hinzu und füllen Sie Objekte aus:
i = 0 n = descriptor.structure.length // Anzahl der Zonen und füllt s = 0, während (i < n ) o = descriptor.structure[i++] o.start = s o.length = o.size * bar_length s += o.length
Für dieses Tutorial, das sind alle Informationen, die wir für unsere Responsive-Music-Lösung benötigen - wir kennen jetzt die Sample-Position und -Länge jeder Zone und füllen die Musik aus. Das bedeutet, dass sie die Zonen und Fills in beliebiger Reihenfolge spielen können wir mögen. Im Wesentlichen können wir jetzt einen unendlich langen Musiktitel zur Laufzeit mit sehr wenig Overhead programmieren.
Nun, da wir alle Informationen haben, die wir zum Abspielen der Musik benötigen, ist das Programmieren von Zonen und Füllungen durch die Musik eine relativ einfache Aufgabe, und wir können dies mit zwei Funktionen erledigen.
Die erste Funktion beschäftigt sich mit der Aufgabe, Samples aus unserer Musikdatei zu ziehen und sie an die Low-Level-Sound-API zu übergeben. Ich werde dies noch einmal anhand von Pseudocode demonstrieren, da verschiedene Programmiersprachen unterschiedliche APIs für diese Art von Dingen haben. Die Theorie ist jedoch in allen Programmiersprachen konsistent.
Eingang // Puffer mit den Samples aus unserer Musikausgabe // Low-Level-Sound-API Ausgabepuffer Abspielkopf = 0 // Position des Abspielkopfs innerhalb der Musikdatei, in Samples Start = 0 // Startposition der aktiven Zone oder Füllung, in Samples length = 0 // Länge der aktiven Zone oder Füllung, in Samples next = null // die nächste Zone oder Füllung (Objekt), die abgespielt werden muss //, wenn die Low-Level-Sound-API weitere Sample-Datenfunktionen erfordert update () i = 0 n = output.length // Samplelänge des Ausgabepuffers end = length - Start solange (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++]
Die zweite Funktion wird verwendet, um die nächste Zone oder den nächsten auszufüllenden Bereich in die Warteschlange zu stellen:
// param 'name' ist der Name der Zone oder Füllung (definiert im Deskriptor) Funktion setNext (name) i = 0 n = descriptor.structure.length // Anzahl der Zonen und füllt, während (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()
Um die "Relaxed" Zone der Musik zu spielen, würden wir anrufen setNext ("Entspannt")
, und die Zone würde in die Warteschlange gestellt und dann bei der nächsten möglichen Gelegenheit gespielt werden.
Das folgende Bild visualisiert die Wiedergabe der Zone 'Relaxed':
Um die "Hunted" Zone der Musik zu spielen, würden wir anrufen setNext ("Hunted")
:
Ob Sie es glauben oder nicht, wir haben jetzt genug zu tun, um einfache, responsive Musik zu jedem Spiel hinzuzufügen, das Zugriff auf eine Low-Level-Sound-API hat, aber es gibt keinen Grund, warum diese Lösung einfach bleiben muss - wir können verschiedene Teile davon spielen die Musik in beliebiger Reihenfolge, die uns gefällt, und das öffnet die Tür zu komplexeren Soundtracks.
Wir könnten zum Beispiel verschiedene Teile der Musik zu Gruppen zusammenfassen, um Sequenzen zu erstellen. Diese Sequenzen könnten als komplexe Übergänge zwischen den verschiedenen Zonen der Musik verwendet werden.
Das Gruppieren verschiedener Teile der Musik zum Erstellen von Sequenzen wird in einem zukünftigen Lernprogramm behandelt. Überlegen Sie sich jedoch, was in der folgenden Abbildung passiert:
Anstatt direkt von einem sehr lauten Musikabschnitt zu einem sehr ruhigen Musikabschnitt zu wechseln, könnten wir die Dinge schrittweise mit einer Sequenz beruhigen, dh einem sanften Übergang.
In diesem Lernprogramm haben wir eine mögliche Lösung für responsive Spielemusik unter Verwendung einer Musikstruktur und eines Musikdeskriptors sowie des für die Wiedergabe der Musik erforderlichen Kerncodes untersucht.
Responsive Music kann ein völlig neues Level des Immersionsspiels in ein Spiel einbringen, und es ist definitiv etwas, das Spieleentwickler in Betracht ziehen sollten, wenn sie mit der Entwicklung eines neuen Spiels beginnen. Spieleentwickler sollten jedoch nicht den Fehler machen, solche Dinge erst in den letzten Entwicklungsstadien zu verlassen. Es erfordert einen koordinierten Aufwand von Spielprogrammierern, Toningenieuren und Designern.