Dobry sposób na odtwarzanie dźwięku, gdy coś się dzieje? Jak to brzmi?

10

Zastanawiałem się więc, jak monolitycznie spędzają czas moje zajęcia. Na przykład w metodzie Characterklasy Jumpmożna mieć odniesienie do obiektu z efektem dźwiękowym i odtwarzać to. To samo w sobie jest w porządku, ale jeśli weźmie się pod uwagę fizykę, animację, kolizję itp., Metoda Jump staje się ogromna, a Characterklasa ma wiele zależności od wielu różnych rzeczy. Mimo to może być w porządku. Co jednak, jeśli nie chcemy już odtwarzać dźwięku, gdy postać skacze? Teraz musimy znaleźć ten konkretny wiersz kodu w pomieszanym bałaganie Jumpkodu i skomentować go lub cokolwiek innego.

Więc ... myślałem ...

Co jeśli zamiast tego istniała jakaś AudioSystemklasa i wszystko, co zrobiła, to subskrybować losowe wydarzenia, którymi interesują się inne klasy. Na przykład Characterklasa może mieć Jumpedzdarzenie (chyba też statyczne), które jest wywoływane w Characterklasie w metodzie. Następnie Characterklasa nie wiedziałaby nic o małym efekcie dźwiękowym, który jest odtwarzany, gdy postać skacze. AudioSystemBędzie tylko ogromna klasa, że programista mógł wycofać się zahaczyć efekty dźwiękowe z pewnych zdarzeń, które dzieją się w grze dzięki wykorzystaniu imprez statycznych. Następnie, jeśli to ma zbyt duży to może być oddzielone do podklasy, takich jak EffectsAudioSystem, BackgroundAudioSystem, AmbientAudioSystem, et cetera.

Następnie, w opcjach gry, można mieć pole wyboru, aby włączyć lub wyłączyć tego rodzaju dźwięki, a wszystko, co trzeba zrobić, to po prostu wyłączyć ten system za pomocą prostej i pojedynczej flagi boolowskiej. Pomysł systemów można również rozszerzyć na takie rzeczy, jak fizyka, animacje itp., Do tego stopnia, że ​​większość reakcji w grze wynikających z działań gracza jest połączona przez te skomplikowane i oddzielone systemy.

Okej, więc moje pytanie może być trochę niejasne, ale jak to brzmi? Tak naprawdę nigdy nie słyszałem o wielu rozmowach na temat tego rodzaju systemu. To wszystko jest teraz w mojej głowie bez żadnego kodowania, więc być może jest to jedna z tych „dobrych w teorii, ale nie w praktyce” transakcji. Czy ten system działałby z większą grą, czy w końcu mógłby się zepsuć i stałby się jeszcze bardziej bałaganem spaghetti niż oryginalny system?

Richard Williams
źródło
5
Brzmi jak dobry pomysł :) (
Mówiąc
1
tak to się robi, powinieneś również umieścić swój rendering w osobnej klasie, jeśli jeszcze go nie masz (jak w, nie powinieneś mieć funkcji draw () w klasie Character).
dreta

Odpowiedzi:

2

Wiadomości są piekłem do debugowania i zarządzania. Brzmi dobrze w teorii, ale po wdrożeniu robi się bałagan z dużą ilością zduplikowanych danych. Efekt dźwiękowy skoku będzie wymagał na końcu dużo więcej danych, na przykład pozycji, prędkości, materiału, na którym znajduje się postać, nazywasz ją, lista będzie długa na końcu.

Więc albo będziesz musiał zebrać te dane i wysłać je do AudioManager za pomocą bardzo konkretnego zdarzenia / wiadomości z skopiowanymi danymi, albo wyślesz odniesienie do znaku w wiadomości, aby AudioManager mógł uzyskać dostęp do danych, zarówno sposoby stają się chaotyczne, a teraz menedżer audio musi wybrać dźwięk dla podziemnego materiału itp.

Tak więc na koniec konkretne wydarzenie (które jest bardzo specyficzną klasą tylko dla tego komunikatu) ponownie połączy te klasy bardzo głęboko. Niewiele wygranych, a na koniec będziesz mieć bałaganiarską dużą listę bardzo specyficznych wydarzeń / klas, które służą tylko do wysyłania danych, które już istnieją i mogą być nieaktualne i będą cierpieć z powodu wszystkich innych problemów z powielonymi danymi .

Będzie więc ogromna lista niepotrzebnych klas do utrzymania, które wprowadzą głębokie sprzężenie między postacią a AudioManagerem, tyle że teraz jest rozproszone po całym kodzie źródłowym. Nie tylko w klasach Character i AudioManager.

Nadal dobrym pomysłem jest oddzielenie kodu, ale Wiadomości to tak naprawdę kolejny sposób głębokich połączeń. Niektóre kody muszą być tylko połączone, użyj najbardziej bezpośredniego sposobu, aby je połączyć, nie nadużywaj inżynierii.

Maik Semder
źródło
1
W jaki sposób dobrze zaprojektowany system przesyłania komunikatów „jest po prostu innym sposobem głębokich połączeń”? Wysyłanie wiadomości jest absolutnym minimalnym sprzężeniem bez obiektów, które nigdy się nie komunikują. Sposób, w jaki został zaprojektowany, może powodować problemy, ale jeśli system przyjmuje tylko dźwięk, lokalizację i typ, rozwiązuje wszystkie jego problemy i nie wprowadza żadnego z sugerowanych przez Ciebie problemów. System audio nie powinien obliczać, jaki dźwięk jest potrzebny, powinien wyodrębnić wszystkie ustawienia, a najlepiej problemy z dyfuzją. en.wikipedia.org/wiki/Coupling_(computer_programming)
ClassicThunder
1
@ClassicThunder Chodzi o to, że w praktyce podejście to nie skaluje się zbyt dobrze. Działa dobrze w przypadku prostych aplikacji i tak długo, jak długo potrzebujesz tylko ogólnego PlaySoundEvent. Ale pytanie dotyczy AudioManager odsłuchującego wyspecjalizowane zdarzenie OnJump (), aby postać mogła pozbyć się pracy audio. Nie będzie to jednak miało miejsca w przypadku prostego PlaySoundEvent, ponieważ postać musi wybrać dźwięk i wysłać go do AudioManager, co unieważnia pierwotny punkt wprowadzenia OnJumpEvent, aby pozbyć się pracy z dźwiękiem.
Maik Semder
1
Jednak podczas korzystania z OnJumpEvent możesz wybrać dodanie odwołania do znaku do zdarzenia lub skopiowanie wszystkich ważnych danych z znaku do zdarzenia. Oczywiście masz rację, ta ostatnia nie wprowadziłaby głębokiego sprzężenia, ale cierpi z powodu problemów z powielaniem danych i nowego obiektu przekazywania danych, który musi być utrzymany, tak jak w przypadku wszystkich innych nowych zdarzeń.
Maik Semder
1
-1 ponieważ zgadzam się z tym, że posiadanie tak wyspecjalizowanych wiadomości (OnJump) jest prawdopodobnie złym pomysłem, ale jest to po prostu długie „Nie, to zły pomysł” zamiast przydatnych informacji, aby skłonić osobę do zrobienia zdarzenia PlaySound, które przyjmuje nazwę efektu dźwiękowego oraz pozycji 3D i / lub głośności, przy której wystąpił.
James
@James dziękuje za wkład, nie chciałem powiedzieć, że używasz zdarzenia PlaySound, chciałem powiedzieć, że wydarzenia są fajną abstrakcją dla aplikacji bazy danych GUI, ale nie są praktyczne w przypadku złożonej gry.
Maik Semder
2

Nie sądzę, żeby system przekazywania wiadomości w ogóle skończył się na inżynierii. W rzeczywistości może to znacznie ułatwić wykonywanie zadań w fazie polskiej. Robisz to dobrze!

To, co opisałeś, jest dokładnie tym, co razem wrzuciłem do naszej gry Global Game Jam w zeszłym roku. Byłem odpowiedzialny za tworzenie i edytowanie SFX oraz integrację muzyki, którą ja i inny kompozytor napisaliśmy w grze w sposób, który nie był do bani.

To, co jest świetne w tym podejściu z perspektywy audio, polega na tym, że pozwala ono robić o wiele więcej interesujących rzeczy z dźwiękiem. Jeśli uważasz, że efekt dźwiękowy w grze to tylko plik dźwiękowy, głośność i panoramowanie, oznacza to, że robisz to źle.

Przykład

W naszej grze byłeś dinozaurem latającym statkiem kosmicznym biegnącym na planety, aby zdobyć punkty. Pracowaliśmy we Flashu, więc infrastruktura oparta na danych nie była potrzebna. AudioManager był klasą składającą się z szeregu metod statycznych, których jedynym celem było kontrolowanie dźwięków, które wydarzyły się w wyniku zdarzenia w grze.

Gdybym miał napisać to w C ++, zajęłoby to trochę więcej czasu, aby wyodrębnić wszystkie możliwe zachowania dźwięków. Wymagania dotyczące komunikatu informującego system o podjęciu działania nie byłyby zbyt skomplikowane. Potrzebowałby po prostu typu wiadomości, wpływu na obiekt pochodzenia lub obiekt, dostępu do pewnego rodzaju kontekstu stanu gry i niewiele więcej. Protokół może rosnąć wraz ze wzrostem potrzeb gry. Oczywiście, jeśli robisz to wszystko w implementacji kodu (jak nasz tandetny kod GGJ), masz gorszy problem z klasą monolityczną. Ale łatwo to złagodzić, tworząc system oparty na danych.

W każdym razie, oto jak nasz system audio do gry zareagował na różne wiadomości:

  • Gracz zderza się z planetą: wywołałoby to dźwięk wybuchu planety, wystarczająco podstawowy. natychmiast po zapytaniu o działający licznik kombinacji. Gdyby był wystarczająco wysoki, zaplanowałby, że efekt dźwiękowy zostanie odtworzony około pół sekundy później dinozaura, który wyda ryk zwycięstwa. Również w tle obliczono losową liczbę ludności planety (coś w rodzaju od 600 do 3000 - nie mam pojęcia, dlaczego ten zakres został wybrany, była to porzucona mechanika rozgrywki i wciąż leżał dookoła, aby użyć mnie, aby dźwięk był interesujący), i wykorzystałem to do skalowania głośności odległego dźwięku krzyków (obywatele planet spotykający przedwczesny los).

  • Gracz przytrzymuje spację w celu przyspieszenia: po otrzymaniu tego odtwarzano dźwięk „steru”, ale jednocześnie słychać było niski ryk silnika narastający przez 1,5 sekundy. Układ cząsteczkowy wykorzystał to również do odpalenia emitera IIRC

  • Gracz puszcza spację w celu spowolnienia: Teraz, gdy gracz puścił spację, system audio wiedział, że musi zwolnić pętlę silnika. Gdybym miał więcej czasu, wolałbym nałożyć na niego kolejny dźwięk, który był rodzajem marudzenia obniżającego dźwięk.

  • Gracz zderza się ze złą miną kosmiczną: miny kosmiczne są złe, więc nie tylko występuje metaliczny dźwięk uderzenia połączony z eksplozją (który jest po prostu wypalony w jednym dźwięku), ale także losowo wybrany dźwięk przerażenia dinozaura. Bardziej prawdopodobne jest, że wybierze więcej „płaczących” dźwięków, gdy zdrowie gracza spadnie.

Już i tak fajna gra staje się przyjemnością, gdy jej ścieżka dźwiękowa jest aktywna i dynamiczna, nawet przy niewielkich prostych zachowaniach, jak opisano powyżej. Tak, istnieje pewna logistyka do dopilnowania, aby upewnić się, że prawidłowe dane zostaną przekazane. Ale hej, BFD. Będzie to dalekie od najbardziej skomplikowanej rzeczy, którą musisz napisać w większym zakresie kodu gry.

W rzeczywistości FMOD i Wwise działają w ten sposób. Nie mają centralnego programu do wysyłania wiadomości, ale skutecznie publikujesz zdarzenia w ich centralnych systemach i reagują, odtwarzając efekt dźwiękowy, który został wcześniej zaprojektowany przez implementatora audio w narzędziu do tworzenia treści. Pomyśl o tym, jakbyś dał swojej grze DJ-a na żywo. Siedzi i obserwuje, co się dzieje, i uruchamia klipy dźwiękowe we właściwym czasie, aby zachować ciekawość, miksując je tak, aby dobrze pasowały do ​​istniejącego środowiska audio.

[EDYCJA] Widzę też, że otagowałeś ten C #. Czy to XNA, a jeśli tak, czy używasz XACT? Jeśli używasz XNA, powinieneś używać XACT.

michael.bartnett
źródło
1
Może to działać w przypadku małego projektu, może nawet być zabawne. Jednak w dużym przypadku powstaje ogromna liczba klas komunikatów, które należy zachować, podczas gdy zwykłe wywołanie funkcji miałoby ten sam efekt. Dlatego system zdarzeń nie skaluje się dobrze, trudniej jest zarządzać tym większym projektem.
Maik Semder
1
Przy okazji pracujemy z FMOD w moim studio, nie ma systemu wiadomości / zdarzeń, nie wysyłasz zdarzeń do FMOD, po prostu wywołujesz funkcję c lub metodę c ++, aby coś odtworzyć. Po prostu nazywają swoje dźwięki „wydarzeniami”, co nie czyni z nich systemu zdarzeń, to tylko termin, którego używają zamiast dźwięku.
Maik Semder
Nie dlaczego? Po prostu wywołujesz funkcję bezpośrednio zamiast używać zdarzenia do przekazania parametrów. Zdarzenie na końcu jest niczym innym jak wywołaniem funkcji, po prostu przekazuje parametry w obiekcie zdarzenia, zamiast przekazywać je bezpośrednio. Jedyną różnicą jest nowa pośrednia wprowadzona przez system zdarzeń, ale na koniec jest to proste wywołanie funkcji, tylko niepotrzebnie nadmiernie skomplikowane.
Maik Semder
@MaikSemder W jaki sposób wywołania metod nie kończą się we własnej splątanej sieci paskudności? Starałem się również zauważyć różnicę między systemem zdarzeń a „zdarzeniami” używanymi przez Wwise i FMOD. Chodzi mi o to, że złożona logika dźwiękowa nie należy do klas obiektów w grach, a wyodrębnienie logiki dźwiękowej w taki sposób, że interfejs jest podobny do wywołania zdarzenia, ułatwia uzyskanie bogatej logiki audio. Naprawdę widzę małą funkcjonalną różnicę między EventManager->dispatch("Sound:PlayerJump")i soundSystem->playFMODEvent("/MyGame/Player/Jump").
michael.bartnett
2
Korzyścią może być łatwiejsze kodowanie, ale nie jest ono bezpłatne, ale wiąże się z kosztami wydajności, utrzymania i trudniejszego debugowania. Chodzi mi o to, że korzyść nie jest tego warta w przypadku dużych projektów. Masz do czynienia ze znacznie większą ilością przedmiotów niż bez wydarzeń i musisz za to zapłacić. Jedynym miejscem, które rozważałbym użycie systemu komunikatów, jest komunikacja między wątkami, aby zapobiec blokowaniu między wątkami.
Maik Semder
0

Zgadzam się z Maikiem Semderem, że system przekazywania wiadomości może być nadmiernie inżynierski (na razie zresztą).

Z tego co rozumiem, klasa obecnie wygląda jak „monolitycznego klasy” Bjorn jak widać w „monolitycznego klasy” tutaj .

Sugeruję przeczytanie tego artykułu i chociaż na razie przesadzenie z systemem składowym byłoby przesadą, to jeśli przeczytasz „Podział reszty”, powinno to dać ci dobry sposób na wyodrębnienie swoich zachowań i ewentualnie przejście do bardziej złożonego rozwiązanie. To da ci dobrą bazę do rozpoczęcia.

spowolniony
źródło