Gute Möglichkeit, einen Sound abzuspielen, wenn etwas passiert? Wie klingt das?

10

Also habe ich darüber nachgedacht, wie monolithisch meine Klassen die meiste Zeit werden. Beispielsweise kann man in der Methode der CharacterKlasse Jumpeinen Verweis auf ein Soundeffektobjekt haben und dieses abspielen. Das ist an sich in Ordnung, aber wenn Physik, Animation, Kollision usw. berücksichtigt werden, wird die Sprungmethode riesig und die CharacterKlasse hat viele Abhängigkeiten von vielen verschiedenen Dingen. Trotzdem kann dies in Ordnung sein. Was ist jedoch, wenn beim Springen des Charakters kein Ton mehr abgespielt werden soll? Jetzt müssen wir diese bestimmte Codezeile im Durcheinander des JumpCodes finden und sie auskommentieren oder was auch immer.

Also .. ich dachte ..

Was wäre, wenn es stattdessen eine Art AudioSystemKlasse gäbe und sie nur zufällige Ereignisse abonnieren würde, an denen sie in anderen Klassen interessiert ist ? Zum Beispiel kann die CharacterKlasse ein JumpedEreignis haben (vermutlich auch statisch), das innerhalb der CharacterKlasse in der Methode ausgelöst wird. Dann Characterwürde die Klasse nichts über den kleinen Soundeffekt wissen, der gespielt wird, wenn der Charakter springt. Das AudioSystemwäre nur eine riesige Klasse, in die sich der Programmierer zurückziehen könnte, um Soundeffekte mit bestimmten Ereignissen, die im Spiel auftreten, mithilfe statischer Ereignisse zu verbinden. Dann wird , wenn es zu groß bekommt könnte es um die Unterklassen wie getrennt werden EffectsAudioSystem, BackgroundAudioSystem, AmbientAudioSystemund so weiter.

Dann könnte man in den Optionen für das Spiel ein Kontrollkästchen haben, um diese Art von Sounds zu aktivieren oder zu deaktivieren, und alles, was getan werden müsste, ist nur, dieses eine System mit einem einfachen und einzelnen Booleschen Flag zu deaktivieren. Diese Idee von Systemen könnte auch auf Dinge wie Physik, Animationen usw. ausgedehnt werden, bis zu dem Punkt, an dem die meisten Spielreaktionen, die sich aus Spieleraktionen ergeben, über diese ausgeklügelten und entkoppelten Systeme miteinander verbunden sind.

Okay, meine Frage mag etwas vage sein, aber wie klingt so etwas? Ich habe noch nie wirklich viel über diese Art von System gesprochen. Das ist alles in meinem Kopf, ohne dass bisher eine Codierung durchgeführt wurde. Vielleicht ist es eines dieser "theoretisch guten, aber nicht praktischen" Geschäfte. Würde diese Art von System mit einem größeren Spiel funktionieren oder würde es irgendwann zusammenbrechen und noch mehr zu einem Spaghetti-Chaos werden als das ursprüngliche System?

Richard Williams
quelle
5
Klingt nach einer guten Idee :) (Ernsthafter: Die Verwendung von Nachrichten für die Kommunikation zwischen lose gekoppelten Subsystemen / Klassen ist im Allgemeinen eine gute Idee in
Bezug auf das
1
So wird es gemacht. Sie sollten Ihr Rendering auch in eine separate Klasse einfügen, falls Sie dies noch nicht getan haben (wie in, Sie sollten keine draw () -Funktion in der Character-Klasse haben).
Dreta

Antworten:

2

Nachrichten sind eine Hölle zum Debuggen und Verwalten. Theoretisch klingt es gut, aber wenn es einmal in die Praxis umgesetzt wird, wird es unordentlich, wenn viele doppelte Daten gesendet werden. Der Jump-Sound-Effekt benötigt am Ende viel mehr Daten, zum Beispiel die Position, Geschwindigkeit, das Material, auf dem sich der Charakter befindet, Sie nennen es, die Liste wird am Ende lang sein.

Entweder müssen Sie diese Daten sammeln und über ein ganz bestimmtes Ereignis / eine bestimmte Nachricht mit den darin kopierten Daten an den AudioManager senden, oder Sie senden einen Verweis auf das Zeichen in der Nachricht, damit der AudioManager auf beide Daten zugreifen kann Die Wege enden chaotisch, und jetzt muss der Audio-Manager einen Sound für das Material-Underground usw. auswählen.

Am Ende wird das spezifische Ereignis (das nur für diese Nachricht eine sehr spezifische Klasse ist) diese Klassen wieder sehr tief koppeln. Nicht viel gewonnen und am Ende haben Sie eine unordentliche große Liste sehr spezifischer Ereignisse / Klassen, die nur dem Zweck dienen, Daten zu versenden, die bereits vorhanden sind und möglicherweise veraltet sind und unter allen anderen Problemen mit doppelten Daten leiden .

Es wird also eine riesige Liste unnötiger Klassen geben, die eine tiefe Kopplung zwischen dem Charakter und dem AudioManager einführen müssen, außer jetzt ist sie über den gesamten Quellcode verteilt. Nicht nur in den Character- und AudioManager-Klassen.

Es ist immer noch eine gute Idee, Ihren Code zu entkoppeln, aber Nachrichten sind wirklich nur eine andere Art der tiefen Kopplung. Einige Codes müssen nur gekoppelt werden. Verwenden Sie den direktesten Weg, um sie zu koppeln. Überentwickeln Sie sie nicht.

Maik Semder
quelle
1
Wie ist ein gut gestaltetes Nachrichtensystem "nur eine andere Art der tiefen Kopplung"? Das Senden von Nachrichten ist die absolute Mindestkopplung, ohne dass die Objekte niemals kommunizieren. Die Art und Weise, wie Sie es entworfen haben, kann Probleme verursachen. Wenn das System jedoch nur einen Ton, einen Ort und einen Typ aufnimmt, werden alle seine Probleme gelöst und es wird keines der von Ihnen vorgeschlagenen Probleme eingeführt. Das Audiosystem sollte nicht berechnen, welcher Ton benötigt wird, sondern alle Einstellungen und vorzugsweise Diffusionsprobleme abstrahieren. en.wikipedia.org/wiki/Coupling_(computer_programming)
ClassicThunder
1
@ClassicThunder Der Punkt ist in der Praxis, dass dieser Ansatz nicht sehr gut skaliert. Es funktioniert gut für einfache Anwendungen und solange Sie nur ein allgemeines PlaySoundEvent benötigen. Die Frage ist jedoch, ob der AudioManager ein spezielles OnJump () - Ereignis abhört, damit der Charakter die Audioarbeit loswerden kann. Dies ist jedoch bei einem einfachen PlaySoundEvent nicht der Fall, da der Charakter den Sound auswählen und an den AudioManager senden muss, wodurch der ursprüngliche Punkt der Einführung des OnJumpEvent ungültig wird, um die Audioarbeit loszuwerden.
Maik Semder
1
Bei Verwendung von OnJumpEvent können Sie jedoch entweder den Verweis auf das Zeichen zum Ereignis hinzufügen oder alle wichtigen Daten des Zeichens in das Ereignis kopieren. Natürlich haben Sie Recht, letzteres würde keine tiefe Kopplung einführen, aber es wird unter Datenvervielfältigungsproblemen und einem neuen Datenübertragungsobjekt leiden, das wie bei allen anderen neuen Ereignissen gepflegt werden muss.
Maik Semder
1
-1 Denn obwohl ich damit einverstanden bin, dass es wahrscheinlich eine schlechte Idee ist, so spezialisierte Nachrichten (OnJump) zu haben, ist dies nur eine lange "Nein, das ist eine schlechte Idee" anstelle nützlicher Informationen, um die Person dazu zu bringen, ein PlaySound-Ereignis zu erstellen, das den Namen trägt eines Soundeffekts und einer 3D-Position und / oder Lautstärke, bei der er aufgetreten ist.
James
@ James, danke für die Eingabe. Ich wollte nicht sagen, dass ich ein PlaySound-Ereignis verwenden soll. Ich wollte sagen, dass Ereignisse eine nette Abstraktion für eine GUI-Datenbankanwendung sind, aber für ein komplexes Spiel nicht praktisch.
Maik Semder
2

Ich glaube nicht, dass ein Message-Passing-System über das Engineering hinausgeht. Tatsächlich kann es dramatisch einfacher sein, Dinge in der polnischen Phase zu erledigen. Du machst es richtig!

Was Sie beschrieben haben, ist genau das, was ich letztes Jahr für unser Global Game Jam-Spiel zusammengestellt habe. Ich war dafür verantwortlich, die SFX zu erstellen und zu bearbeiten und Musik, die ich und ein anderer Komponist geschrieben haben, auf eine Art und Weise in das Spiel zu integrieren, die nicht schlecht war.

Das Besondere an diesem Ansatz aus Audio-Sicht ist, dass Sie so viele weitere interessante Dinge mit Ihrem Sound tun können. Wenn Sie der Meinung sind, dass ein Soundeffekt in einem Spiel lediglich eine Audiodatei, Lautstärke und ein Schwenken ist, machen Sie es falsch.

Beispiel

Für unser Spiel waren Sie ein Dinosaurier, der ein Raumschiff flog, das auf Planeten lief, um Punkte zu sammeln. Wir haben in Flash gearbeitet, daher wurde keine datengesteuerte Infrastruktur benötigt. Der AudioManager war eine Klasse, die aus einer Reihe statischer Methoden bestand, deren einziger Zweck darin bestand, zu steuern, welche Geräusche als Reaktion auf ein Spielereignis auftraten.

Wenn ich es in C ++ schreiben würde, hätte es etwas länger gedauert, alle möglichen Verhaltensweisen zu abstrahieren, die Sounds haben könnten. Die Anforderungen für eine Nachricht, die das System darüber informiert, dass eine Aktion stattgefunden hat, wären nicht zu kompliziert. Es würde nur den Nachrichtentyp, das Ursprungsobjekt oder das betroffene Objekt, den Zugriff auf eine Art Spielstatuskontext und nicht viel mehr benötigen. Das Protokoll könnte mit den Anforderungen des Spiels wachsen. Wenn Sie all dies bei der Implementierung in Code tun (wie bei unserem schlechten GGJ-Code), haben Sie natürlich ein schlimmeres monolithisches Klassenproblem. Dies lässt sich jedoch leicht durch die Erstellung eines datengesteuerten Systems abmildern.

Wie auch immer, unser Spiel-Audiosystem hat auf verschiedene Nachrichten reagiert:

  • Spieler kollidiert mit dem Planeten: Dies würde einen Planetenexplosionsklang auslösen, der grundlegend genug ist. dann unmittelbar danach würde es den laufenden Kombinationszähler abfragen. Wenn es hoch genug wäre, würde es einen Soundeffekt einplanen, der ungefähr eine halbe Sekunde später gespielt wird, wenn der Dinosaurier ein Siegesgebrüll macht. Auch im Hintergrund wurde ein zufälliger Planetenpopulationswert berechnet (etwa 600 bis 3000 - ich habe keine Ahnung, warum dieser Bereich gewählt wurde, es war eine verlassene Spielmechanik und lag immer noch herum, damit ich das Audio interessant machen konnte). und so benutzte ich das, um die Lautstärke des entfernten Geräusches von Schreien zu skalieren (Planetenbürger, die einem vorzeitigen Schicksal begegnen).

  • Der Spieler hält die Leertaste für die Beschleunigung gedrückt : Nach Erhalt dieses Signals wurde ein kleiner "Whoosh" -Trieb abgespielt, gleichzeitig wurde jedoch ein Motorgeräusch mit niedriger Schleife über 1,5 Sekunden hochgefahren. Das Partikelsystem verwendete dies auch, um einen Emitter IIRC zu feuern

  • Der Spieler lässt die Leertaste zum Abbremsen los: Nachdem der Spieler die Leertaste losgelassen hatte, wusste das Audiosystem, dass die Motorschleife wieder heruntergefahren werden musste. Wenn ich mehr Zeit gehabt hätte, hätte ich gerne einen anderen Sound darüber gelegt, der eine Art jammerndes Geräusch war.

  • Der Spieler kollidiert mit der bösen Weltraummine: Weltraumminen sind schlecht, daher gibt es nicht nur einen metallischen Aufprallklang in Kombination mit einer Explosion (die nur in einem Klang zusammengefasst ist), sondern auch einen zufällig ausgewählten Dinosaurier-Bestürzungsklang, der abgespielt wird. Es ist wahrscheinlicher, dass Sie mehr "Weinen" wie Geräusche wählen, wenn die Gesundheit des Spielers abnimmt.

Ein bereits unterhaltsames Spiel wird zu einer Freude, wenn sein Soundtrack aktiv und dynamisch ist, selbst mit nur wenigen einfachen Verhaltensweisen, wie ich sie oben beschrieben habe. Ja, es gibt einige logistische Aspekte, um sicherzustellen, dass die richtigen Daten übergeben werden. Aber hey, BFD. Es wird alles andere als das Komplizierteste sein, was Sie im größeren Bereich des Spielcodes schreiben müssen.

Tatsächlich funktionieren FMOD und Wwise so. Sie haben keinen zentralen Nachrichten-Dispatcher, aber Sie veröffentlichen Ereignisse effektiv auf ihren zentralen Systemen und sie reagieren, indem sie einen Soundeffekt abspielen, der von einem Audio-Implementierer in einem Authoring-Tool vorab entworfen wurde. Stellen Sie sich vor, Sie geben Ihrem Spiel einen Live-DJ. Er sitzt und beobachtet, was los ist, und löst zum richtigen Zeitpunkt Soundclips aus, um die Dinge interessant zu halten, und mischt sie so, dass sie gut in die bereits vorhandene Audioumgebung passen.

[BEARBEITEN] Ich sehe auch, dass Sie dieses C # markiert haben. Ist das XNA und wenn ja, verwenden Sie XACT? Wenn Sie XNA verwenden, sollten Sie XACT verwenden.

michael.bartnett
quelle
1
Dies könnte für ein kleines Projekt funktionieren, es könnte sogar Spaß machen. In einem großen Fall haben Sie jedoch eine große Anzahl von Nachrichtenklassen, die beibehalten werden müssen, während ein einfacher Funktionsaufruf den gleichen Effekt gehabt hätte. Aus diesem Grund lässt sich das Ereignissystem nicht gut skalieren. Je größer das Projekt wird, desto schwieriger wird es, es zu verwalten.
Maik Semder
1
Übrigens arbeiten wir in meinem Studio mit FMOD, es gibt kein Nachrichten- / Ereignissystem, Sie senden keine Ereignisse an FMOD, Sie rufen einfach eine C-Funktion oder eine C ++ - Methode auf, um etwas abzuspielen. Sie nennen ihre Sounds einfach "Ereignisse", was sie nicht zu einem Ereignissystem macht, sondern nur den Begriff, den sie anstelle von Sound verwenden.
Maik Semder
Nein, warum? Sie rufen die Funktion einfach direkt auf, anstatt ein Ereignis zum Übergeben der Parameter zu verwenden. Ein Ereignis ist am Ende nichts anderes als ein Funktionsaufruf, der nur die Parameter im Ereignisobjekt übergibt, anstatt sie direkt zu übergeben. Der einzige Unterschied ist die neue Indirektion, die vom Ereignissystem eingeführt wird, aber am Ende ist es ein einfacher Funktionsaufruf, der nur unnötig überkompliziert ist.
Maik Semder
@MaikSemder Wie landen Methodenaufrufe nicht in ihrem eigenen Netz von Gemeinheiten? Außerdem habe ich versucht, diese Unterscheidung zwischen einem Ereignissystem und den von Wwise und FMOD verwendeten "Ereignissen" zu notieren. Die Idee, auf die ich komme, ist, dass komplexe Audiologik nicht zu Spielobjektklassen gehört. Wenn Sie die Soundlogik so abstrahieren, dass die Schnittstelle dem Auslösen eines Ereignisses ähnelt, wird es einfacher, über eine umfassende Audiologik zu verfügen. Ich sehe wirklich wenig funktionalen Unterschied zwischen EventManager->dispatch("Sound:PlayerJump")und soundSystem->playFMODEvent("/MyGame/Player/Jump").
michael.bartnett
2
Es könnte den Vorteil einer einfacheren Codierung geben, diese ist jedoch nicht kostenlos, sondern mit Kosten für Leistung, Wartung und härteres Debuggen verbunden. Mein Punkt ist, der Nutzen lohnt sich nicht für große Projekte. Sie haben es mit weit mehr Objekten als ohne Ereignisse zu tun und müssen den Preis dafür bezahlen. Der einzige Ort, an dem ich die Verwendung eines Nachrichtensystems in Betracht ziehen würde, ist die Kommunikation zwischen Threads, um ein Sperren zwischen Threads zu verhindern.
Maik Semder
0

Ich stimme Maik Semder zu, dass ein Nachrichtenübermittlungssystem (vorerst sowieso) möglicherweise überentwickelt ist.

Soweit ich weiß, sieht Ihre Klasse derzeit wie die "monolithische Klasse" von Björn aus, wie in "Eine monolithische Klasse" hier zu sehen ist .

Ich schlage vor, Sie lesen diesen Artikel, und obwohl ein Vollkomponentensystem vorerst übertrieben wäre, sollte dies Ihnen eine gute Möglichkeit bieten, Ihr Verhalten zu abstrahieren und möglicherweise zu einem komplexeren zu wechseln, wenn Sie "Den Rest aufteilen" lesen Lösung. Es gibt Ihnen eine gute Basis, um trotzdem zu beginnen.

verzögert kaviar
quelle