Un bon moyen de jouer un son quand quelque chose se passe? Comment ça sonne?

10

Je pensais donc à la monolithicité de mes cours la plupart du temps. Par exemple, dans la méthode de la Characterclasse Jump, on peut avoir une référence à un objet d'effet sonore et le jouer. En soi, c'est bien, mais quand la physique, l'animation, les collisions, etc. sont prises en compte, la méthode Jump devient énorme et la Characterclasse a beaucoup de dépendances avec beaucoup de choses différentes. Pourtant, cela peut être bien. Cependant, que se passe-t-il si nous ne voulons plus qu'un son soit joué lorsque le personnage saute? Maintenant, nous devons trouver cette ligne de code spécifique dans le désordre du Jumpcode et le commenter ou quoi que ce soit.

Alors .. je pensais ..

Et si, à la place, il y avait une sorte de AudioSystemclasse et tout ce qu'elle faisait était de s'abonner à des événements aléatoires qui l'intéressaient dans d'autres classes. Par exemple, la Characterclasse peut avoir un Jumpedévénement (statique aussi, je suppose) qui est déclenché dans la Characterclasse dans la méthode. Ensuite, la Characterclasse ne saurait rien du petit effet sonore qui est joué lorsque le personnage saute. Ce AudioSystemserait juste une énorme classe dans laquelle le programmeur pourrait se retirer pour connecter des effets sonores à certains événements qui se produisent dans le jeu grâce à l'utilisation d'événements statiques. Ensuite, si elle devenait trop grand , il pourrait être séparé en sous - classes comme EffectsAudioSystem, BackgroundAudioSystem, AmbientAudioSystemet ainsi de suite.

Ensuite, dans les options du jeu, on pourrait avoir une case à cocher pour activer ou désactiver ces sortes de sons et tout ce qui devrait être fait est simplement de désactiver ce système avec un drapeau booléen simple et unique. Cette idée de systèmes pourrait également être étendue à des choses comme la physique, les animations, etc. au point où la plupart des réponses de jeu résultant des actions des joueurs sont connectées à travers ces systèmes élaborés et découplés.

D'accord, donc ma question peut être un peu vague, mais comment ce genre de chose sonne-t-il? Je n'ai jamais vraiment entendu parler de ce genre de système. Tout cela est dans ma tête en ce moment sans aucun codage fait jusqu'à présent, donc c'est peut-être une de ces transactions "bonnes en théorie mais pas en pratique". Ce type de système fonctionnerait-il avec un jeu plus grand ou finirait-il par tomber en panne et devenir encore plus un gâchis de spaghetti que le système d'origine?

Richard Williams
la source
5
Cela ressemble à une bonne idée :) (Sur une note plus sérieuse: l'utilisation de messages pour la communication entre des sous-systèmes / classes faiblement couplés est généralement une bonne idée du point de vue de la conception)
bummzack
1
c'est comme ça que vous faites, vous devriez aussi mettre votre rendu dans une classe séparée, si vous ne l'avez pas déjà fait (comme dans, vous ne devriez pas avoir de fonction draw () dans la classe Character).
dreta

Réponses:

2

Les messages sont un enfer à déboguer et à maintenir. Cela semble bon en théorie, mais une fois mis en pratique, cela devient compliqué avec beaucoup de données en double envoyées. L'effet de son de saut nécessitera beaucoup plus de données à la fin, par exemple la position, la vitesse, le matériau sur lequel le personnage est, vous le nommez, la liste sera longue à la fin.

Donc, soit vous devrez collecter ces données et les envoyer à l'AudioManager via un événement / message très spécifique avec les données copiées, soit vous enverrez une référence au personnage du message, afin que l'AudioManager puisse accéder aux données, à la fois les façons finissent en désordre, et maintenant le gestionnaire audio doit choisir un son pour le matériau underground, etc.

Donc, à la fin, l'événement spécifique (qui est une classe très spécifique uniquement pour ce message) couplera à nouveau ces classes très profondément. Pas beaucoup gagné et à la fin, vous aurez une grande liste désordonnée d'événements / classes très spécifiques qui ne servent qu'à envoyer des données, qui existent déjà, et peuvent être obsolètes et souffriront de tous les autres problèmes de données dupliquées .

Il y aura donc une énorme liste de classes inutiles à maintenir qui introduisent un couplage profond entre le personnage et l'AudioManager, sauf que maintenant il est dispersé partout dans le code source. Pas seulement dans les classes Character- et AudioManager.

C'est toujours une bonne idée de découpler votre code, mais les messages ne sont vraiment qu'une autre façon de couplages profonds. Il suffit de coupler du code, d'utiliser le moyen le plus direct pour les coupler, ne pas trop ingénier.

Maik Semder
la source
1
Comment un système de messagerie bien conçu est-il "juste un autre moyen de couplage profond"? L'envoi de messages est le couplage minimum absolu sans que les objets ne communiquent jamais. La façon dont vous l'avez conçu peut provoquer des problèmes, mais si le système n'accepte qu'un son, un emplacement et un type, il résout tous ses problèmes et n'introduit aucun de ceux que vous suggérez. Le système audio ne doit pas calculer le son dont il a besoin, il doit éliminer tous les paramètres et de préférence les problèmes de diffusion. en.wikipedia.org/wiki/Coupling_(computer_programming)
ClassicThunder
1
@ClassicThunder le fait est que dans la pratique cette approche n'est pas très bien adaptée. Il fonctionne bien pour les applications simples et tant que vous n'avez besoin que d'un PlaySoundEvent général. Mais la question concerne l'AudioManager écoutant un événement OnJump () spécialisé, afin que le personnage puisse se débarrasser du travail audio. Ce ne sera cependant pas le cas avec un simple PlaySoundEvent puisque le personnage doit choisir le son et l'envoyer à l'AudioManager, ce qui invalide le point d'origine d'introduire l'OnJumpEvent pour se débarrasser du travail audio.
Maik Semder
1
Cependant, lorsque vous utilisez OnJumpEvent, vous pouvez choisir d'ajouter la référence au personnage à l'événement ou de copier toutes les données importantes du personnage dans l'événement. Bien sûr, vous avez raison, ce dernier n'introduirait pas de couplage profond, mais il souffrira de problèmes de duplication de données et d'un nouvel objet de passage de données qui doit être maintenu, comme pour tous les autres nouveaux événements.
Maik Semder
1
-1 parce que je suis d'accord que le fait d'avoir des messages aussi spécialisés (OnJump) est probablement une mauvaise idée, ce n'est qu'un long 'Non c'est une mauvaise idée' au lieu d'informations utiles pour amener la personne à créer un événement PlaySound qui porte le nom d'un effet sonore et d'une position et / ou d'un volume 3D auxquels il s'est produit.
James
@James merci pour l'entrée, je ne voulais pas dire utiliser un événement PlaySound, je voulais dire que les événements sont une belle abstraction pour une application de base de données GUI, mais pas pratique pour un jeu complexe.
Maik Semder
2

Je ne pense pas qu'un système de transmission de messages soit du tout sur l'ingénierie. En fait, cela peut faciliter considérablement la réalisation des tâches pendant la phase de polissage. Tu le fais bien!

Ce que vous avez décrit est exactement ce que j'ai jeté ensemble pour notre jeu Global Game Jam l'année dernière. J'étais responsable de la création et de l'édition du SFX, et de l'intégration de la musique que moi-même et un autre compositeur avons écrite dans le jeu d'une manière qui ne craignait pas.

Ce qui est génial avec cette approche d'un point de vue audio, c'est qu'elle vous permet de faire beaucoup plus de choses intéressantes avec votre son. Si vous pensez qu'un effet sonore dans un jeu est simplement un fichier son, un volume et un panoramique, vous vous trompez.

Exemple

Pour notre jeu, vous étiez un dinosaure pilotant un vaisseau spatial courant sur des planètes pour marquer des points. Nous travaillions dans Flash, donc une infrastructure basée sur les données n'était pas nécessaire. L'AudioManager était une classe composée d'un tas de méthodes statiques dont le seul but était de contrôler ce qui se passait en réponse à un événement de jeu.

Si je devais l'écrire en C ++, il aurait fallu un peu plus de temps pour résumer tous les comportements possibles des sons. Les exigences pour un message informant le système qu'une action a eu lieu ne seraient pas trop compliquées. Il aurait juste besoin du type de message, de l'objet d'origine ou de l'objet affecté, de l'accès à une sorte de contexte d'état du jeu, et pas grand-chose d'autre. Le protocole pourrait évoluer en fonction des besoins du jeu. Naturellement, si vous faites tout cela en implémentation dans du code (comme notre code GGJ de mauvaise qualité), vous avez un problème de classe monolithique pire. Mais cela est facilement atténué en créant un système basé sur les données.

Quoi qu'il en soit, voici comment notre système audio de jeu a réagi à divers messages:

  • Le joueur entre en collision avec la planète: Cela déclencherait un bruit d'explosion de la planète, assez basique. puis immédiatement après, il interrogerait le compteur combo en cours d'exécution. S'il était suffisamment élevé, il programmerait un effet sonore à jouer une demi-seconde environ plus tard du dinosaure faisant un rugissement de victoire. Également en arrière-plan, une valeur aléatoire de la population de la planète a été calculée (quelque chose comme 600 à 3000 - je n'ai aucune idée de la raison pour laquelle cette plage a été choisie, c'était une mécanique de gameplay abandonnée et toujours à utiliser pour rendre l'audio intéressant), et donc je l'ai utilisé pour mettre à l'échelle le volume du son lointain des cris (les citoyens planétaires rencontrant un destin inopportun).

  • Le joueur tient la barre d'espace pour l'accélération: À la réception de ce petit bruit de propulseur "whoosh" a été joué, mais aussi simultanément un rugissement du moteur en boucle basse a augmenté en plus de 1,5 secondes. Le système de particules a également utilisé cela pour tirer un émetteur IIRC

  • Le joueur lâche la barre d'espace pour la décélération: Maintenant que le joueur a lâché la barre d'espace, le système audio savait qu'il devait ralentir la boucle du moteur. Si j'avais plus de temps, j'aurais aimé superposer un autre son dessus qui était une sorte de pleurnicher type de son.

  • Le joueur entre en collision avec une mine spatiale maléfique: les mines spatiales sont mauvaises, donc non seulement il y a un bruit d'impact métallique combiné à une explosion (qui est juste cuit en un seul son), mais il y a aussi un son de consternation de dinosaure sélectionné au hasard qui joue. Il est plus probable de choisir des sons plus «pleurants» à mesure que la santé du joueur diminue.

Un jeu déjà amusant devient un plaisir à jouer lorsque sa bande-son est active et dynamique, même avec peu de comportements simples comme je l'ai décrit ci-dessus. Oui, il y a une logistique à régler pour s'assurer que les données correctes sont transmises. Mais bon, BFD. Ce sera loin d'être la chose la plus compliquée à écrire dans le cadre plus large du code du jeu.

En fait, FMOD et Wwise fonctionnent comme ça. Ils n'ont pas de répartiteur de messages central, mais vous postez efficacement les événements sur leurs systèmes centraux et ils réagissent en jouant un effet sonore qui a été pré-conçu par un implémenteur audio dans un outil de création. Pensez-y comme donner à votre jeu un DJ en direct. Il s'assoit et regarde ce qui se passe, et déclenche des extraits sonores au bon moment pour garder les choses intéressantes, les mixant pour qu'elles s'intègrent bien dans l'environnement audio préexistant.

[EDIT] De plus, je vois que vous avez tagué ce C #. S'agit-il de XNA, et si oui, utilisez-vous XACT? Si vous utilisez XNA, vous devez utiliser XACT.

michael.bartnett
la source
1
Cela pourrait fonctionner pour un petit projet, cela pourrait même être amusant. Cependant, dans une grande, vous vous retrouvez avec un grand nombre de classes de messages, qui doivent être maintenues, alors qu'un simple appel de fonction aurait eu le même effet. C'est pourquoi le système événementiel n'est pas bien évolutif, il devient difficile de gérer plus le projet est gros.
Maik Semder
1
Btw nous travaillons avec FMOD dans mon studio, il n'y a pas de système de message / événement, vous n'envoyez pas d'événements à FMOD, vous appelez simplement une fonction c ou une méthode c ++ pour jouer quelque chose. Ils appellent simplement leurs sons "événements", cela n'en fait pas un système d'événements, c'est juste le terme qu'ils utilisent au lieu de son.
Maik Semder
Non pourquoi? Vous appelez simplement la fonction directement au lieu d'utiliser un événement pour passer les paramètres. Un événement n'est à la fin rien d'autre qu'un appel de fonction, il suffit de passer les paramètres dans l'objet événement, au lieu de les passer directement. La seule différence est la nouvelle indirection introduite par le système d'événements, mais à la fin c'est un simple appel de fonction, seulement inutilement trop compliqué.
Maik Semder
@MaikSemder Comment les appels de méthode ne se retrouvent-ils pas dans leur propre réseau de méchanceté enchevêtré? Aussi, j'ai essayé de noter cette distinction entre un système d'événements et les «événements» utilisés par Wwise et FMOD. L'idée à laquelle je veux en venir est que la logique audio complexe n'appartient pas aux classes d'objets de jeu, et l'abstraction de la logique sonore de sorte que l'interface s'apparente à la distribution d'un événement facilite la richesse de la logique audio. Je vois vraiment peu de différence fonctionnelle entre EventManager->dispatch("Sound:PlayerJump")et soundSystem->playFMODEvent("/MyGame/Player/Jump").
michael.bartnett
2
Il pourrait y avoir l'avantage d'un codage plus facile, mais ce n'est pas gratuit, il s'accompagne de coûts de performances, de maintenance et de débogage plus difficiles. Mon point est que l'avantage n'en vaut pas la peine pour les grands projets. Vous traitez bien plus d'objets que sans événements et vous devez en payer le prix. Le seul endroit où j'envisagerais d'utiliser un système de messagerie est la communication inter-threads pour empêcher le verrouillage entre les threads.
Maik Semder
0

Je suis d'accord avec Maik Semder, qu'un système de transmission de messages peut être une ingénierie excessive (pour l'instant de toute façon).

D'après ce que je comprends, votre classe ressemble actuellement à la "classe monolithique" de Bjorn, comme on peut le voir dans "Une classe monolithique" ici .

Je vous suggère de lire cet article et, même si un système complet de composants serait excessif pour l'instant, si vous lisez "Répartir le reste", cela devrait vous donner un bon moyen d'abstraire vos comportements et éventuellement de passer à un système plus complexe. Solution. Cela vous donnera une bonne base pour commencer de toute façon.

caviar décéléré
la source