Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Programming

Dynamische, sequentielle Soundtracks für Spiele

by
Difficulty:IntermediateLength:LongLanguages:

German (Deutsch) translation by Valentina (you can also view the original English article)

In diesem Tutorial werfen wir einen Blick auf eine Technik zum Konstruieren und Sequenzieren dynamischer Musik für Spiele. Die Konstruktion und Sequenzierung erfolgt zur Laufzeit, sodass Spieleentwickler die Struktur der Musik ändern können, um das Geschehen in der Spielwelt widerzuspiegeln.

Bevor wir uns mit den technischen Details befassen, möchten Sie vielleicht eine Funktionsdemonstration dieser Technik in Aktion betrachten. Die Musik in der Demonstration besteht aus einer Sammlung einzelner Audioblöcke, die zur Laufzeit sequenziert und gemischt werden, um die gesamte Musikspur zu bilden.

Click to view the demo.
Klicken Sie hier, um die Demo anzuzeigen.

Diese Demonstration erfordert einen Webbrowser, der die W3C Web Audio API und OGG-Audio unterstützt. Google Chrome ist der beste Browser zum Anzeigen dieser Demonstration, Firefox Aurora kann jedoch auch verwendet werden.

Wenn Sie die obige Demo nicht in Ihrem Browser anzeigen können, können Sie stattdessen dieses YouTube-Video ansehen:



Überblick

Die Art und Weise, wie diese Technik funktioniert, ist ziemlich unkompliziert, aber sie hat das Potenzial, wirklich kreative Musik zu Spielen hinzuzufügen, wenn sie kreativ eingesetzt wird. Außerdem können unendlich lange Musiktitel aus einer relativ kleinen Audiodatei erstellt werden.

Die ursprüngliche Musik ist im Wesentlichen in eine Sammlung von Blöcken zerlegt, von denen jeder einen Takt lang ist, und diese Blöcke werden in einer einzigen Audiodatei gespeichert.  Der Musiksequenzer lädt die Audiodatei und extrahiert die rohen Audio-Samples, die zur Rekonstruktion der Musik benötigt werden. Die Struktur der Musik wird durch eine Sammlung von veränderbaren Arrays vorgegeben, die dem Sequenzer mitteilen, wann er die Musikblöcke spielen soll.

Sie können sich diese Technik als eine vereinfachte Version von Sequenziersoftware wie Reason, FL Studio oder Dance EJay vorstellen. Sie können sich diese Technik auch als das musikalische Äquivalent von Legosteinen vorstellen.


Audio-Dateistruktur

Wie bereits erwähnt, muss der Musiksequenzer die Originalmusik in eine Sammlung von Blöcken zerlegen, und diese Blöcke müssen in einer Audiodatei gespeichert werden.

Dieses Bild zeigt, wie die Blöcke in einer Audiodatei gespeichert werden können.

In diesem Bild können Sie sehen, dass fünf einzelne Blöcke in der Audiodatei gespeichert sind und alle Blöcke gleich lang sind. Um die Dinge für dieses Tutorial einfach zu halten, sind die Blöcke alle einen Takt lang.

Die Reihenfolge der Blöcke in der Audiodatei ist wichtig, da sie bestimmt, welchen Sequenzerkanälen die Blöcke zugeordnet sind. Der erste Block (z. B. Schlagzeug) wird dem ersten Sequenzerkanal zugewiesen, der zweite Block (z. B. Percussion) wird dem zweiten Sequenzerkanal zugewiesen und so weiter.


Sequenzerkanäle

Ein Sequenzerkanal stellt eine Reihe von Blöcken dar und enthält Flags (eine für jeden Musikbalken), die angeben, ob der dem Kanal zugewiesene Block gespielt werden soll. Jedes Flag ist ein numerischer Wert und ist entweder null (Block nicht abspielen) oder Eins (Block spielen).

Dieses Bild zeigt die Beziehung zwischen den Blöcken und den Sequenzerkanälen.

Die Zahlen, die horizontal am unteren Rand des obigen Bildes ausgerichtet sind, stehen für Taktzahlen. Wie Sie sehen, wird im ersten Takt der Musik (01) nur der Guitar-Block gespielt, im fünften Takt (05) werden jedoch die Drums-, Percussion-, Bass- und Guitar-Blöcke gespielt.


Programmierung

In diesem Tutorial werden wir nicht den Code eines voll funktionsfähigen Musiksequenzers durchgehen. Stattdessen betrachten wir den Kerncode, der erforderlich ist, um einen einfachen Musiksequenzer zum Laufen zu bringen. Der Code wird als Pseudocode dargestellt, um die Dinge so sprachunabhängig wie möglich zu halten.

Bevor wir beginnen, müssen Sie bedenken, dass die Programmiersprache, die Sie letztendlich verwenden möchten, eine API erfordert, mit der Sie Audio auf einem niedrigen Niveau bearbeiten können. Ein gutes Beispiel dafür ist die in JavaScript verfügbare Web-Audio-API.

Sie können auch die an dieses Tutorial angehängten Quelldateien herunterladen, um eine JavaScript-Implementierung eines grundlegenden Musiksequenzers zu studieren, der als Demonstration für dieses Tutorial erstellt wurde.

Schneller Überblick

Wir haben eine einzige Audiodatei, die Musikblöcke enthält. Jeder Musikblock ist einen Takt lang, und die Reihenfolge der Blöcke in der Audiodatei bestimmt den Sequenzerkanal, dem die Blöcke zugewiesen sind.

Konstanten

Es gibt zwei Informationen, die wir benötigen, bevor wir fortfahren können. Wir müssen das Tempo der Musik in Beats pro Minute und die Anzahl der Beats in jedem Takt kennen.  Letzteres kann als die Taktart der Musik angesehen werden. Diese Informationen sollten als konstante Werte gespeichert werden, da sie sich nicht ändern, während der Musiksequenzer läuft.

Wir müssen auch die Abtastrate kennen, die die Audio-API verwendet. Normalerweise sind dies 44100 Hz, da dies für das Audiosignal vollkommen in Ordnung ist, aber einige Leute haben ihre Hardware für eine höhere Abtastrate konfiguriert.  Die Audio-API, die Sie verwenden, sollte diese Informationen enthalten. Für dieses Tutorial gehen wir jedoch von einer Samplerate von 44100 Hz aus.

Wir können nun die Sample-Länge eines Musikbalkens berechnen, dh die Anzahl der Audio-Samples in einem Musikblock. Dieser Wert ist wichtig, da der Musiksequenzer die einzelnen Musikblöcke und die Audio-Samples innerhalb jedes Blocks in den Audiodatei-Daten lokalisieren kann.

Audio-Streamsx

Audio-StreamsDie Audio-API, die Sie verwenden, bestimmt, wie Audiostreams (Arrays von Audio-Samples) in Ihrem Code dargestellt werden. Beispielsweise verwendet die Web-Audio-API AudioBuffer-Objekte.

Für dieses Tutorial gibt es zwei Audio-Streams. Der erste Audiostrom ist schreibgeschützt und enthält alle Audio-Samples, die aus der Audiodatei mit den Musikblöcken geladen wurden. Das ist der Audiostrom "Eingabe".

Der zweite Audiostrom ist schreibgeschützt und wird verwendet, um Audio-Samples auf die Hardware zu pushen. Dies ist der Audiostream "Ausgabe". Jeder dieser Ströme wird als eindimensionales Array dargestellt.

Der genaue Vorgang, der zum Laden der Audiodatei und zum Extrahieren der Audio-Samples aus der Datei erforderlich ist, hängt von der verwendeten Programmiersprache ab. In diesem Zusammenhang nehmen wir an, dass das input-Audio-Stream-Array bereits die aus der Audiodatei extrahierten Audio-Samples enthält.

Der output-Audiostrom hat normalerweise eine feste Länge, da Sie bei den meisten Audio-APIs die Frequenz auswählen können, mit der die Audio-Samples verarbeitet und an die Hardware gesendet werden müssen, das heißt, wie oft eine update-Funktion aufgerufen wird. Die Frequenz ist normalerweise direkt an die Latenzzeit des Audios gebunden. Hohe Frequenzen erfordern mehr Prozessorleistung, führen jedoch zu niedrigeren Latenzzeiten und umgekehrt.

Sequenzerdaten

Die Sequenzerdaten sind ein mehrdimensionales Array. Jedes Subarray stellt einen Sequenzerkanal dar und enthält Flags (eine für jeden Musikstrich), die angeben, ob der dem Kanal zugewiesene Musikblock abgespielt werden soll oder nicht. Die Länge der Channel-Arrays bestimmt auch die Länge der Musik.

Die Daten, die Sie dort sehen, stellen eine Musikstruktur dar, die sechzehn Takte lang ist. Es enthält fünf Kanäle, einen für jeden Musikblock in der Audiodatei, und die Kanäle befinden sich in derselben Reihenfolge wie die Musikblöcke in der Audiodatei.  Die Flags in den Channel-Arrays geben an, ob der den Channels zugewiesene Block gespielt werden soll oder nicht: Der Wert 0 bedeutet, dass ein Block nicht gespielt wird. Der Wert 1 bedeutet, dass ein Block gespielt wird.

Diese Datenstruktur ist veränderbar. Sie kann jederzeit geändert werden, auch wenn der Musiksequenzer läuft. Dadurch können Sie die Flags und die Struktur der Musik ändern, um die Ereignisse in einem Spiel wiederzugeben.

Audioverarbeitung

Die meisten Audio-APIs senden entweder ein Ereignis an eine Event-Handler-Funktion oder rufen eine Funktion direkt auf, wenn mehr Audio-Samples auf die Hardware übertragen werden müssen. Diese Funktion wird normalerweise wie die Hauptaktualisierungsschleife eines Spiels ständig aufgerufen, jedoch nicht so häufig. Daher sollte Zeit für die Optimierung aufgewendet werden.

Grundsätzlich passiert bei dieser Funktion:

  1. Mehrere Audio-Samples werden aus dem input-Audiostrom abgerufen.
  2. Diese Samples werden zu einem einzigen Audio-Sample zusammengefügt.
  3. Dieses Audio-Sample wird in den output-Audiostream verschoben.

Bevor wir uns der Funktion nähern, müssen wir ein paar weitere Variablen im Code definieren:

Das playing Boolean lässt uns einfach wissen, ob die Musik spielt. Wenn es nicht abgespielt wird, müssen wir stille Audio-Samples in den output-Audiostream stecken. Die position überwacht, wo sich der Abspielkopf in der Musik befindet, also ist es ein bisschen wie ein Scrubber auf einem typischen Musik- oder Videoplayer.

Nun zu den Dingen der Funktion:

Wie Sie sehen, ist der für die Verarbeitung der Audio-Samples erforderliche Code relativ einfach. Da dieser Code jedoch mehrmals pro Sekunde ausgeführt wird, sollten Sie nach Möglichkeiten suchen, den Code innerhalb der Funktion zu optimieren und so viele Werte wie möglich im Voraus zu berechnen. Die Optimierungen, die Sie auf den Code anwenden können, hängen nur von der verwendeten Programmiersprache ab.

Vergessen Sie nicht, dass Sie die an dieses Tutorial angehängten Quelldateien herunterladen können, wenn Sie eine Möglichkeit zum Implementieren eines grundlegenden Musiksequenzers in JavaScript mithilfe der Web-Audio-API anzeigen möchten.


Anmerkungen

Das Format der von Ihnen verwendeten Audiodatei muss einen nahtlosen Loop-Verlauf ermöglichen. Mit anderen Worten, der zum Generieren der Audiodatei verwendete Encoder sollte keine Auffüllung (stille Audiostücke) in die Audiodatei einfügen.  Leider können MP3- und MP4-Dateien aus diesem Grund nicht verwendet werden. OGG-Dateien (die von der JavaScript-Demonstration verwendet werden) können verwendet werden.  Sie können auch WAV-Dateien verwenden, wenn Sie möchten, aber sie sind aufgrund ihrer Größe für webbasierte Spiele oder Anwendungen nicht sinnvoll.

Wenn Sie ein Spiel programmieren und die Programmiersprache, die Sie für das Spiel verwenden, Parallelität (Fäden oder Arbeiter) unterstützt, möchten Sie möglicherweise den Audioverarbeitungscode in einem eigenen Fäden oder Arbeiter ausführen, wenn dies möglich ist.  Dadurch wird die Hauptaktualisierungsschleife des Spiels von eventuell auftretenden Audio-Verarbeitungsaufwand befreit.


Dynamische Musik in beliebten Spielen

Im Folgenden finden Sie eine kleine Auswahl beliebter Spiele, die dynamische Musik auf die eine oder andere Weise nutzen. Die Implementierung, die diese Spiele für ihre dynamische Musik verwenden, kann variieren, aber das Endergebnis ist dasselbe: Die Spieler des Spiels haben ein intensiveres Spielerlebnis.


Schlussfolgerung

Also, los geht's - eine einfache Implementierung von dynamischer sequentieller Musik, die die emotionale Natur eines Spiels wirklich verbessern kann. Wie Sie sich für diese Technik entscheiden und wie komplex der Sequenzer wird, liegt ganz bei Ihnen.  Es gibt viele Richtungen, die diese einfache Implementierung annehmen kann, und wir werden einige dieser Richtungen in einem zukünftigen Lernprogramm behandeln.

Wenn Sie Fragen haben, können Sie diese gerne in den folgenden Kommentaren posten. Ich werde mich so schnell wie möglich bei Ihnen melden.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.