Un buon modo per riprodurre un suono quando succede qualcosa? Come suona?


10

Quindi stavo pensando a quanto monolitico le mie lezioni passino molto tempo. Ad esempio, nel metodo Characterdella classe Jump, si può avere un riferimento a un oggetto di effetto sonoro e riprodurlo. Di per sé va bene, ma quando si tiene conto di fisica, animazione, collisione, ecc., Il metodo Jump diventa enorme e la Characterclasse ha molte dipendenze da molte cose diverse. Tuttavia, potrebbe andare bene. Tuttavia, cosa succede se non vogliamo più riprodurre un suono quando il personaggio salta? Ora, dobbiamo trovare quella specifica riga di codice nel pasticcio confuso del Jumpcodice e commentarlo o altro.

Quindi .. stavo pensando ..

E se invece ci fosse una sorta di AudioSystemclasse e tutto ciò che ha fatto è stato iscriversi a eventi casuali a cui è interessato in altre classi. Ad esempio, la Characterclasse potrebbe avere un Jumpedevento (anche statico, suppongo) che viene generato all'interno della Characterclasse nel metodo. Quindi, la Characterclasse non saprebbe nulla del piccolo effetto sonoro che viene riprodotto quando il personaggio salta. Il AudioSystemsarebbe solo una classe enorme che il programmatore potrebbe ritirarsi a collegare gli effetti sonori con alcuni eventi che accadono nel gioco attraverso l'utilizzo di eventi statici. Poi, se si fosse troppo grande potrebbe essere separato per sottoclassi come EffectsAudioSystem, BackgroundAudioSystem, AmbientAudioSystem, eccetera.

Quindi, nelle opzioni per il gioco, si potrebbe avere una casella di controllo per abilitare o disabilitare questo tipo di suoni e tutto ciò che dovrebbe essere fatto è solo disabilitare quel sistema con un semplice e singolo flag booleano. Questa idea di sistemi potrebbe anche essere estesa a cose come la fisica, le animazioni, ecc. Al punto in cui la maggior parte delle risposte al gioco risultanti dalle azioni dei giocatori sono collegate attraverso questi sistemi elaborati e disaccoppiati.

Ok, quindi la mia domanda potrebbe essere un po 'vaga, ma come suona questo genere di cose? Non ho mai sentito parlare di molti tipi di discorsi su questo tipo di sistema. Questo è tutto nella mia testa in questo momento senza alcuna codifica fatta finora, quindi forse è uno di quei tipi di affari "buoni in teoria ma non in pratica". Questo tipo di sistema funzionerebbe con un gioco più grande o alla fine si guasterebbe e diventerebbe ancora più un pasticcio di spaghetti rispetto al sistema originale?


5
Sembra una buona idea :) (In una nota più seria: usare messaggi per comunicare tra sottosistemi / classi vagamente accoppiati è generalmente una buona idea dal punto di vista del design)
bummzack

1
in questo modo, dovresti mettere anche il tuo rendering in una classe separata, se non hai già (come in, non dovresti avere una funzione draw () nella classe Character).
dreta,

Risposte:


2

I messaggi sono un inferno da debug e da mantenere. In teoria suona bene, ma una volta messo in pratica diventa confuso con un sacco di dati duplicati inviati in giro. L'effetto sonoro di salto avrà bisogno di molti più dati alla fine, ad esempio la posizione, la velocità, il materiale su cui si trova il personaggio, il nome, l'elenco alla fine sarà lungo.

Quindi o dovrai raccogliere questi dati e inviarli ad AudioManager tramite un evento / messaggio molto specifico con i dati copiati in esso o invierai un riferimento al carattere nel messaggio, in modo che AudioManager possa accedere ai dati, sia i modi finiscono per essere disordinati, e ora l'audio manager deve scegliere un suono per il materiale underground, ecc.

Quindi alla fine l'evento specifico (che è una classe molto specifica solo per questo messaggio) abbinerà di nuovo quelle classi molto in profondità. Non molto vinto e alla fine avrai un grande elenco disordinato di eventi / classi molto specifici che servono solo allo scopo di inviare dati in giro, che esiste già, e potrebbero essere obsoleti e soffriranno di tutti gli altri problemi di dati duplicati .

Quindi ci sarà un vasto elenco di classi non necessarie da mantenere che introducono un profondo accoppiamento tra il personaggio e AudioManager, tranne per il fatto che ora è sparso in tutto il codice sorgente. Non solo nelle classi Character e AudioManager.

È comunque una buona idea disaccoppiare il codice, ma i messaggi sono in realtà solo un altro modo di accoppiamenti profondi. È necessario accoppiare un po 'di codice, utilizzare il modo più diretto per accoppiarli, non sovraccaricare.


1
In che modo un sistema di messaggistica ben progettato "è solo un altro modo di accoppiamenti profondi"? L'invio di messaggi è l'accoppiamento minimo assoluto senza che gli oggetti non comunichino mai. Il modo in cui lo hai progettato potrebbe causare problemi, ma se il sistema rileva solo un suono, una posizione e un tipo, risolve tutti i suoi problemi e introduce nessuno di quelli che suggerisci possano verificarsi. Il sistema audio non dovrebbe calcolare quale suono è necessario, dovrebbe sottrarre tutte le impostazioni e preferibilmente i problemi di diffusione. en.wikipedia.org/wiki/Coupling_(computer_programming)
ClassicThunder

1
@ClassicThunder il punto è in pratica questo approccio non si adatta molto bene. Funziona bene per applicazioni semplici e basta che sia un PlaySoundEvent generale. Ma la domanda riguarda l'audioManager che ascolta un evento specializzato OnJump (), quindi il personaggio può sbarazzarsi del lavoro audio. Questo tuttavia non sarà il caso di un semplice PlaySoundEvent poiché il personaggio deve scegliere il suono e inviarlo all'AudioManager, il che invalida il punto originale dell'introduzione di OnJumpEvent per sbarazzarsi del lavoro audio.
Maik Semder,

1
Quando si utilizza OnJumpEvent, tuttavia, è possibile scegliere di aggiungere il riferimento al personaggio all'evento o copiare tutti i dati importanti dal personaggio nell'evento. Naturalmente hai ragione, quest'ultimo non introdurrebbe un accoppiamento profondo, ma soffrirà di problemi di duplicazione dei dati e di un nuovo oggetto di passaggio di dati che deve essere mantenuto, come per tutti gli altri nuovi eventi.
Maik Semder,

1
-1 causa mentre sono d'accordo che avere messaggi così specializzati (OnJump) è probabilmente una cattiva idea, questa è solo una lunga "No questa è una cattiva idea" invece di informazioni utili per convincere la persona a creare un evento PlaySound che prende il nome di un effetto sonoro e una posizione 3D e / o volume in cui si è verificato.
James,

@James grazie per l'input, non intendevo dire di usare un evento PlaySound, intendevo dire che gli eventi sono una bella astrazione per un'applicazione con database GUI, ma non pratici per un gioco complesso.
Maik Semder,

2

Non penso affatto che un sistema di trasmissione di messaggi sia finito in ingegneria. In effetti, può semplificare notevolmente le operazioni nella fase polacca. Lo stai facendo bene!

Quello che hai descritto è esattamente quello che ho messo insieme per il nostro gioco Global Game Jam dell'anno scorso. Ero responsabile della creazione e del montaggio di SFX e dell'integrazione della musica che io e un altro compositore abbiamo scritto nel gioco in un modo che non faceva schifo.

La cosa grandiosa di questo approccio dal punto di vista audio è che ti permette di fare molte cose più interessanti con il tuo suono. Se pensi che un effetto sonoro in un gioco sia semplicemente un file audio, un volume e una panoramica, allora stai sbagliando.

Esempio

Per il nostro gioco eri un dinosauro che pilotava un'astronave che correva su pianeti per guadagnare punti. Lavoravamo in Flash, quindi non era necessaria un'infrastruttura basata sui dati. L'AudioManager era una classe costituita da un gruppo di metodi statici il cui unico scopo era controllare quali suoni sono accaduti in risposta a un evento di gioco.

Se dovessi scriverlo in C ++, ci vorrebbe un po 'più di tempo per astrarre tutti i possibili comportamenti che i suoni potrebbero avere. I requisiti per un messaggio di notifica al sistema di un'azione non sarebbero troppo complicati. Avrebbe solo bisogno del tipo di messaggio, dell'oggetto di origine o dell'oggetto interessato, accesso a un qualche tipo di contesto dello stato del gioco e non molto altro. Il protocollo potrebbe crescere man mano che crescono le esigenze del gioco. Naturalmente, se fai tutto questo nell'implementazione nel codice (come il nostro scadente codice GGJ), hai un problema di classe monolitica peggiore. Ma questo è facilmente mitigato creando un sistema basato sui dati.

Ad ogni modo, ecco come il nostro sistema audio di gioco ha reagito a vari messaggi:

  • Il giocatore si scontra con il pianeta: questo innescherebbe un suono di esplosione del pianeta, abbastanza semplice. quindi immediatamente dopo avrebbe interrogato il contatore combo in esecuzione. Se fosse abbastanza alto, avrebbe programmato un effetto sonoro da suonare circa mezzo secondo dopo che il dinosauro faceva un ruggito di vittoria. Anche sullo sfondo è stato calcolato un valore casuale della popolazione planetaria (qualcosa come 600 a 3000 - Non ho idea del perché sia ​​stata scelta quella gamma, era una meccanica di gioco abbandonata e ancora in giro per me da usare per rendere l'audio interessante), e così l'ho usato per aumentare il volume del suono distante delle urla (i cittadini planetari incontrano un destino prematuro).

  • Il giocatore tiene la barra spaziatrice per l'accelerazione: alla ricezione di questo è stato riprodotto un piccolo suono di elica "whoosh", ma anche simultaneamente un rombo del motore a ciclo basso è aumentato di oltre 1,5 secondi. Il sistema particellare lo usava anche per sparare un emettitore IIRC

  • Il giocatore lascia andare la barra spaziatrice per la decelerazione: ora che il giocatore aveva lasciato andare la barra spaziatrice, il sistema audio sapeva che doveva riattivare il circuito del motore. Se avessi avuto più tempo mi sarebbe piaciuto sovrapporre un altro suono che fosse una sorta di piagnucolio che lanciava un tipo di suono.

  • Il giocatore si scontra con una mina spaziale malvagia: le mine spaziali sono cattive, quindi non solo c'è un suono di impatto metallico combinato con un'esplosione (che è appena stata cotta in un suono), ma c'è anche un suono di sgomento di dinosauro selezionato casualmente che suona. È più probabile che scelga più "pianti" come suoni man mano che la salute del giocatore diminuisce.

Un gioco già divertente diventa un piacere da giocare quando la sua colonna sonora è attiva e dinamica, anche con solo piccoli comportamenti semplici come ho descritto sopra. Sì, ci sono alcuni aspetti logistici da elaborare per assicurarsi che vengano trasmessi i dati corretti. Ma ehi, BFD. Sarà ben lungi dall'essere la cosa più complicata che devi scrivere nel più ampio ambito del codice di gioco.

In effetti, FMOD e Wwise funzionano così. Non hanno un dispatcher di messaggi centrale, ma in effetti pubblichi eventi sui loro sistemi centrali e reagiscono riproducendo un effetto sonoro che è stato pre-progettato da un implementatore audio in uno strumento di creazione. Pensa a come dare al tuo gioco un DJ dal vivo. Si siede e guarda cosa sta succedendo e innesca clip audio nei momenti giusti per mantenere le cose interessanti, mescolandole in modo che si adattino bene all'ambiente audio preesistente.

[EDIT] Inoltre, vedo che hai taggato questo C #. Questo è XNA, e se è così stai usando XACT? Se stai usando XNA dovresti usare XACT.


1
Questo potrebbe funzionare per un piccolo progetto, potrebbe anche essere divertente. Tuttavia in una grande si finisce con un numero enorme di classi di messaggi, che devono essere mantenute, mentre una semplice chiamata di funzione avrebbe avuto lo stesso effetto. Ecco perché il sistema degli eventi non si adatta bene, diventa difficile gestire più grande diventa il progetto.
Maik Semder,

1
Tra l'altro lavoriamo con FMOD nel mio studio, non esiste un sistema di messaggi / eventi, non invii eventi a FMOD, chiami semplicemente una funzione c o un metodo c ++ per riprodurre qualcosa. Chiamano semplicemente i loro suoni "eventi", che non lo rendono un sistema di eventi, è solo il termine che usano al posto del suono.
Maik Semder,

No perchè? Basta chiamare direttamente la funzione invece di utilizzare un evento per passare i parametri. Un evento alla fine non è nient'altro che una chiamata di funzione, passa semplicemente i parametri nell'oggetto evento, invece di passarli direttamente. L'unica differenza è la nuova indiretta introdotta dal sistema di eventi, ma alla fine è una semplice chiamata di funzione, solo inutilmente troppo complicata.
Maik Semder,

@MaikSemder In che modo le chiamate ai metodi non finiscono nella loro intricata rete di cattiveria? Inoltre, ho cercato di prendere nota di quella distinzione tra un sistema di eventi e gli "eventi" utilizzati da Wwise e FMOD. L'idea a cui sto arrivando è che la complessa logica audio non appartiene alle classi degli oggetti di gioco e astrarre la logica del suono in modo tale che l'interfaccia sia simile all'invio di un evento rende più facile avere una ricca logica audio. Vedo davvero poca differenza funzionale tra EventManager->dispatch("Sound:PlayerJump")e soundSystem->playFMODEvent("/MyGame/Player/Jump").
michael.bartnett,

2
Potrebbe esserci il vantaggio di una codifica più semplice, ma non è gratuito, viene fornito con costi di prestazioni, manutenzione e debug più difficili. Il mio punto è che il vantaggio non vale per grandi progetti. Hai a che fare con molti più oggetti che senza eventi e devi pagarne il prezzo. L'unico posto in cui prenderei in considerazione l'uso di un sistema di messaggi è la comunicazione tra thread per impedire il blocco tra i thread.
Maik Semder,

0

Concordo con Maik Semder, secondo cui un sistema di trasmissione di messaggi potrebbe essere eccessivamente ingegneristico (per ora comunque).

Da quanto ho capito, la tua classe attualmente assomiglia alla "classe monolitica" di Bjorn, come si può vedere in "Una classe monolitica" qui .

Ti suggerisco di leggere quell'articolo e, sebbene per ora un sistema a componenti completi sarebbe eccessivo, se leggi "Dividere il resto", ciò dovrebbe darti un buon modo per astrarre i tuoi comportamenti e, eventualmente, passare a un sistema più complesso soluzione. Ti darà comunque una buona base da cui partire.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.