¿Buena manera de reproducir un sonido cuando sucede algo? ¿Como suena esto?

10

Así que estaba pensando en cuán monolíticas mis clases se vuelven mucho tiempo. Por ejemplo, en el método de la Characterclase Jump, uno puede tener una referencia a un objeto de efecto de sonido y reproducirlo. Eso en sí mismo está bien, pero cuando se tiene en cuenta la física, la animación, la colisión, etc., el método Jump se vuelve enorme y la Characterclase tiene muchas dependencias para muchas cosas diferentes. Aún así, esto puede estar bien. Sin embargo, ¿qué pasa si ya no queremos que suene un sonido cuando el personaje salta? Ahora, tenemos que encontrar esa línea específica de código en la confusión del Jumpcódigo y comentarlo o lo que sea.

Entonces ... estaba pensando ...

¿Qué pasa si, en cambio, hubo algún tipo de AudioSystemclase y todo lo que hizo fue suscribirse a eventos aleatorios que le interesan en otras clases? Por ejemplo, la Characterclase puede tener un Jumpedevento (también estático, supongo) que se genera dentro de la Characterclase en el método. Entonces, la Characterclase no sabría nada sobre el pequeño efecto de sonido que se reproduce cuando el personaje salta. El AudioSystemno sería más que una clase enorme que el programador podría retirarse a conectar efectos de sonido con ciertos acontecimientos que suceden en el juego a través del uso de eventos estáticos. Entonces, si se hacía demasiado grande que podría ser separado en las subclases como EffectsAudioSystem, BackgroundAudioSystem, AmbientAudioSystem, etcétera.

Luego, en las opciones para el juego, uno podría tener una casilla de verificación para habilitar o deshabilitar este tipo de sonidos y todo lo que tendría que hacer es deshabilitar ese sistema con una bandera booleana simple y única. Esta idea de los sistemas también podría extenderse a cosas como la física, las animaciones, etc., hasta el punto en que la mayoría de las respuestas del juego resultantes de las acciones de los jugadores se conectan a través de estos sistemas elaborados y desacoplados.

Bien, entonces mi pregunta puede ser un poco vaga, pero ¿cómo suena este tipo de cosas? Realmente nunca he oído hablar mucho sobre este tipo de sistema. Todo esto está en mi cabeza en este momento sin ninguna codificación realizada hasta ahora, por lo que tal vez sea uno de esos tipos de acuerdos "buenos en teoría pero no en la práctica". ¿Funcionaría este tipo de sistema con un juego más grande?

Richard Williams
fuente
55
Suena como una buena idea :) (En una nota más seria: el uso de mensajes para la comunicación entre subsistemas / clases sueltos generalmente es una buena idea en cuanto al diseño)
bummzack
1
así es como se hace, también debe colocar su renderizado en una clase separada, si aún no lo ha hecho (como en, no debería tener una función draw () en la clase Character).
Dreta

Respuestas:

2

Los mensajes son un infierno para depurar y mantener. Suena bien en teoría, pero una vez que se pone en práctica se vuelve complicado con un montón de datos duplicados que se envían. El efecto de sonido de salto necesitará muchos más datos al final, por ejemplo, la posición, la velocidad, el material en el que se encuentra el personaje, lo que sea, la lista será larga al final.

Por lo tanto, deberá recopilar estos datos y enviarlos al AudioManager a través de un evento / mensaje muy específico con los datos copiados, o enviará una referencia al personaje en el mensaje, para que el AudioManager pueda acceder a los datos, tanto las formas terminan siendo desordenadas, y ahora el administrador de audio tiene que elegir un sonido para el material subterráneo, etc.

Entonces, al final, el evento específico (que es una clase muy específica solo para este mensaje) volverá a acoplar esas clases muy profundamente. No ganó mucho y al final tendrá una gran lista desordenada de eventos / clases muy específicos que solo sirven para enviar datos, que ya existen y que pueden estar desactualizados y sufrirán todos los demás problemas de datos duplicados .

Por lo tanto, habrá una gran lista de clases innecesarias para mantener que introducen un acoplamiento profundo entre el personaje y el AudioManager, excepto que ahora está disperso por todo el código fuente. No solo en las clases Character y AudioManager.

Sigue siendo una buena idea desacoplar su código, pero los mensajes son realmente otra forma de acoplamientos profundos. Algunos códigos solo tienen que estar acoplados, use la forma más directa para acoplarlos, no haga demasiados ingenieros.

Maik Semder
fuente
1
¿Cómo es un sistema de mensajería bien diseñado "simplemente otra forma de acoplamientos profundos"? Enviar mensajes es el acoplamiento mínimo absoluto sin que los objetos nunca se comuniquen. La forma en que lo diseñó puede causar problemas, pero si el sistema solo toma un sonido, una ubicación y un tipo, resuelve todos sus problemas e introduce ninguno de los que sugiere que pueda ocurrir. El sistema de audio no debe calcular qué sonido se necesita, debe abstraer todos los ajustes y, preferiblemente, los problemas de difusión. en.wikipedia.org/wiki/Coupling_(computer_programming)
ClassicThunder
1
@ClassicThunder el punto es que en la práctica este enfoque no escala muy bien. Funciona bien para aplicaciones simples y siempre que todo lo que necesite sea un PlaySoundEvent general. Pero la pregunta es sobre el AudioManager escuchando un evento especializado OnJump (), para que el personaje pueda deshacerse del trabajo de audio. Sin embargo, este no será el caso con un simple PlaySoundEvent ya que el personaje tiene que elegir el sonido y enviarlo al AudioManager, lo que invalida el punto original de introducir el OnJumpEvent para deshacerse del trabajo de audio.
Maik Semder
1
Sin embargo, al usar OnJumpEvent, puede elegir agregar la referencia al personaje al evento o copiar todos los datos importantes del personaje en el evento. Por supuesto que tiene razón, este último no introduciría un acoplamiento profundo, pero sufrirá problemas de duplicación de datos y un nuevo objeto de paso de datos que debe mantenerse, como para todos los demás eventos nuevos.
Maik Semder
1
-1 porque, si bien estoy de acuerdo en que tener mensajes tan especializados (OnJump) es probablemente una mala idea, esto es solo un largo 'No, esta es una mala idea' en lugar de información útil para que la persona haga un evento de PlaySound que tome el nombre de un efecto de sonido y una posición 3D y / o volumen en el que ocurrió.
James
@ James, gracias por el aporte, no quise decir que use un evento PlaySound, quise decir que los eventos son una buena abstracción para una aplicación de base de datos GUI, pero no son prácticos para un juego complejo.
Maik Semder
2

No creo que un sistema de transmisión de mensajes haya terminado la ingeniería. De hecho, puede hacer que sea mucho más fácil hacer las cosas en la fase de pulido. ¡Lo estás haciendo bien!

Lo que describiste es exactamente lo que reuní para nuestro juego Global Game Jam el año pasado. Fui responsable de crear y editar el SFX, e integrar la música que yo y otro compositor escribimos en el juego de una manera que no apestaba.

Lo bueno de este enfoque desde una perspectiva de audio es que te permite hacer muchas cosas más interesantes con tu sonido. Si crees que un efecto de sonido en un juego es simplemente un archivo de sonido, volumen y panorámica, entonces lo estás haciendo mal.

Ejemplo

Para nuestro juego, eras un dinosaurio volando una nave espacial que se topaba con planetas para ganar puntos. Estábamos trabajando en Flash, por lo que no era necesaria una infraestructura basada en datos. El AudioManager era una clase que constaba de un montón de métodos estáticos cuyo único propósito era controlar qué sonidos ocurrían en respuesta a un evento del juego.

Si lo escribiera en C ++, me habría llevado un poco más de tiempo abstraer todos los comportamientos posibles que podrían tener los sonidos. Los requisitos para un mensaje que notifique al sistema que se ha llevado a cabo una acción no serían demasiado complicados. Solo necesitaría el tipo de mensaje, el objeto de origen o el objeto afectado, el acceso a algún tipo de contexto de estado del juego y no mucho más. El protocolo podría crecer a medida que crecen las necesidades del juego. Naturalmente, si hace todo esto en la implementación en código (como nuestro código GGJ de mala calidad), tiene un problema de clase monolítico peor. Pero eso se mitiga fácilmente al crear un sistema basado en datos.

De todos modos, así es como nuestro sistema de audio del juego reaccionó a varios mensajes:

  • El jugador choca con el planeta: esto provocaría un sonido de explosión planetaria, lo suficientemente básico. luego, inmediatamente después de consultar el contador combinado en ejecución. Si fuera lo suficientemente alto, programaría un efecto de sonido que se reproduciría medio segundo más o menos después de que el dinosaurio hiciera un rugido de victoria. También en el fondo se calculó un valor aleatorio de la población del planeta (algo así como 600 a 3000; no tengo idea de por qué se eligió ese rango, era una mecánica de juego abandonada y todavía estaba por ahí para que yo use para hacer que el audio sea interesante), así que usé eso para escalar el volumen del sonido distante de los gritos (ciudadanos planetarios que se encuentran con un destino inoportuno).

  • El jugador tiene la barra espaciadora para la aceleración: al recibir esto, se escuchó un pequeño sonido de propulsor "whoosh", pero también simultáneamente un rugido de motor de bucle bajo aumentó durante 1.5 segundos. El sistema de partículas también usó esto para disparar un emisor IIRC

  • El jugador suelta la barra espaciadora para desacelerar: ahora que el jugador había soltado la barra espaciadora, el sistema de audio sabía que tenía que volver a bajar el circuito del motor. Si tuviera más tiempo, me hubiera gustado superponer otro sonido que fuera una especie de sonido quejumbroso.

  • El jugador choca con la mina espacial malvada: las minas espaciales son malas, por lo que no solo hay un sonido de impacto metálico combinado con una explosión (que se convierte en un solo sonido), sino que también se reproduce un sonido de consternación de dinosaurio seleccionado al azar. Es más probable que elija más sonidos de "llanto" a medida que la salud del jugador disminuye.

Un juego ya divertido se convierte en un placer jugar cuando su banda sonora es activa y dinámica, incluso con comportamientos simples como los que describí anteriormente. Sí, hay algo de logística con la que asegurarse de que se pasen los datos correctos. Pero bueno, BFD. Estará lejos de ser lo más complicado que tiene que escribir en el alcance más amplio del código del juego.

De hecho, FMOD y Wwise funcionan así. No tienen un despachador de mensajes central, pero efectivamente publica eventos en sus sistemas centrales y reaccionan al reproducir un efecto de sonido que fue diseñado previamente por un implementador de audio en una herramienta de autoría. Piense en ello como darle a su juego un DJ en vivo. Se sienta y observa lo que está sucediendo, y activa clips de sonido en los momentos correctos para mantener las cosas interesantes, mezclándolas para que encajen bien en el entorno de audio preexistente.

[EDITAR] Además, veo que has etiquetado este C #. ¿Es esto XNA, y si es así, estás usando XACT? Si está utilizando XNA, debería estar utilizando XACT.

michael.bartnett
fuente
1
Esto podría funcionar para un proyecto pequeño, incluso podría ser divertido. Sin embargo, en uno grande, termina con una gran cantidad de clases de mensajes, que deben mantenerse, mientras que una simple llamada de función habría tenido el mismo efecto. Es por eso que el sistema de eventos no escala bien, se hace difícil de administrar a medida que el proyecto se hace más grande.
Maik Semder
1
Por cierto, trabajamos con FMOD en mi estudio, no hay un sistema de mensajes / eventos, no envías eventos a FMOD, simplemente llamas a una función c o un método c ++ para reproducir algo. Simplemente llaman a sus sonidos "eventos", eso no lo convierte en un sistema de eventos, es solo el término que usan en lugar de sonido.
Maik Semder
¿No porque? Simplemente llame a la función directamente en lugar de usar un evento para pasar los parámetros. Al final, un evento no es más que una llamada de función, solo pasa los parámetros en el objeto de evento, en lugar de pasarlos directamente. La única diferencia es la nueva indirección introducida por el sistema de eventos, pero al final es una simple llamada a la función, que solo es innecesariamente complicada.
Maik Semder
@MaikSemder ¿Cómo las llamadas a métodos no terminan en su propia red de maldad? Además, intenté tomar nota de esa distinción entre un sistema de eventos y los "eventos" utilizados por Wwise y FMOD. La idea a la que me refiero es que la lógica de audio compleja no pertenece a las clases de objetos del juego, y abstraer la lógica de sonido de modo que la interfaz sea similar al envío de un evento hace que sea más fácil tener una lógica de audio rica. Realmente veo poca diferencia funcional entre EventManager->dispatch("Sound:PlayerJump")y soundSystem->playFMODEvent("/MyGame/Player/Jump").
michael.bartnett
2
Puede haber el beneficio de una codificación más fácil, pero no es gratis, viene con costos de rendimiento, mantenimiento y depuración más difíciles. Mi punto es que el beneficio no vale la pena para grandes proyectos. Estás tratando con más objetos que sin eventos y tienes que pagar el precio por ellos. El único lugar en el que consideraría usar un sistema de mensajes es para la comunicación entre subprocesos para evitar el bloqueo entre subprocesos.
Maik Semder
0

Estoy de acuerdo con Maik Semder, en que un sistema de paso de mensajes puede ser una ingeniería excesiva (de todos modos, por ahora).

Por lo que entiendo, su clase actualmente se parece a la "clase monolítica" de Bjorn como se puede ver en "Una clase monolítica" aquí .

Le sugiero que lea ese artículo y, aunque un sistema de componentes completos sería excesivo por ahora, si lee "Separar el resto", eso debería darle una buena forma de abstraer sus comportamientos y, finalmente, quizás pasar a un sistema más complejo solución. Le dará una buena base para comenzar de todos modos.

caviar desacelerado
fuente