Quelle est la méthode standard pour synchroniser les effets sonores avec les animations de sprites?

8

Prenons une situation où vous avez un RPG avec des sorts et chaque animation de sorts a un nombre différent d'images et leurs exigences en matière d'effets sonores sont très différentes. Supposons que chaque sort n'ait qu'une seule animation continue qui lui est associée (par opposition à plusieurs pièces modulaires qui sont utilisées pour former une animation complète) ala les anciens jeux Final Fantasy 16 bits.

La seule façon dont je peux penser afin de m'assurer que la synchronisation des sons et des animations est de:

  • Obtenez le nombre d'images d'une animation.
  • Obtenez le temps entre chaque image de l'animation. (si c'est 30 ips, alors c'est 1 / 30ème de seconde par image.)
  • Créez ensuite un fichier son exactement de la même longueur que l'animation.

Cela signifie donc que si une animation dure 5 secondes, s'exécutant à 30 ips, avec un total de 150 images, le fichier son sera également de 5 secondes. Si l'animation doit avoir un son "d'impact" sur la 30ème image, cela signifie que le fichier son inclura le son d'impact à la marque de 1,0 seconde.

À la fin, nous démarrons l'animation et l'effet sonore exactement en même temps et espérons que les images et le son se synchroniseront.

Cela peut sembler poser des problèmes lorsque des images sont sautées ou que quelque chose se produit pendant l'animation et que le son est joué un peu trop tôt ou trop tard, et entraînera la désynchronisation du son et de l'animation. Est-ce la meilleure approche ou existe-t-il généralement une meilleure façon que je ne vois tout simplement pas?

La réponse ne doit pas nécessairement être pour Cocos2D spécifiquement si elle est conceptuelle, mais s'il existe une solution spécifique pour cocos2d, j'aimerais l'entendre.

EDIT: Je réalise aussi qu'avec cette méthode, si nous entrons et ajustons le nombre d'images ou le timing de l'animation plus tard, nous devons également revenir en arrière et changer le fichier son. Cela ressemble à une terrible cause d'erreur humaine (oublier de mettre à jour les fichiers audio après le changement d'animation.) J'espère qu'il existe de meilleures méthodes.

Jamornh
la source
C'est pourquoi les boucles de jeu à temps constant sont pratiques: alors vous n'avez pas à vous soucier de la désynchronisation de l'animation
Ratchet Freak
@ratchetfreak Je pense que cocos2d gérera correctement le timing de l'animation. Si je crée une animation et que je dis à cocos2d que je veux exactement 1 / 30ème de seconde entre les images, cela assurera cela et sautera les images si les performances ne sont pas assez bonnes. Cela garantit que l'animation est terminée au bon moment (c'est-à-dire en temps constant). Étant donné que, dites-vous que la méthode que j'ai décrite ci-dessus est la bonne voie à suivre?
Jamornh

Réponses:

6

Faites-le via des événements.

Spell begin est un événement . Commencez à jouer le son de cet événement.

Un ennemi frappé par un sort est également un événement. Si l'ennemi est plus éloigné et que vous lancez une fléchette, par exemple, vous ne jouez le deuxième son (fléchette frappant) qu'une fois que la fléchette atteint la cible (si vous considérez lancer comme un sort).

Si vous avez besoin de le lier à une image (par exemple, jouez le son "exploser" dans 30 images à partir de maintenant, indépendamment des fréquences d'images en temps réel )), la façon la plus simple de le faire est d'utiliser les rappels . Les rappels ne sont que des "blocs de code que vous prévoyez d'exécuter à l'avenir". Voici un exemple de mon setter de création de callback:

- (void) addCallback:(Callback*)callback inHowManyTicks:(unsigned long long)execTicksIntoTheFuture
{
  callbacks.push_back( new TimedCallback( tick + execTicksIntoTheFuture, callback ) ) ;
}

A TimedCallbackest juste un wrapper autour d'un std::function(ou vous pouvez utiliser un Objective-C ^{block}. Le std::functionest exécuté lorsque son cadre est en place.

Une autre façon (moins globale) consiste à inclure des événements dans votre animation . Donc, si vous devez beaucoup jouer des sons spécifiques sur des images d'animation spécifiques, stockez- map<int,Sound*>en un dans la Animationclasse. Sur chaque image de l'animation, vérifiez s'il y a un correspondant Sound*à jouer pour cette image.

Vous pouvez également stocker un map<int, std::function>, dans l' Animationobjet, ce qui vous permettrait de rappeler un functionpendant l'animation.

bobobobo
la source
Je pense que vous avez peut-être mal interprété la question légèrement. Votre méthode fonctionnerait pour les sons courts destinés aux types "punch", "hit", "kick", "shoot" qui ne durent pas plus d'une fraction de seconde où la synchronisation n'est pas un problème (vous pouvez simplement jouer l'effet sonore "sur événement "comme vous l'avez suggéré.) Cependant, avec une longue animation + son (par exemple armageddon, où 5-6 météores tombent du ciel et touchent le sol à différentes images, mais font partie d'une animation de sprite [comme la fantaisie finale les fait] n'aura qu'un seul événement de démarrage, pas un par météorite) cette méthode n'assurera pas la synchronisation, n'est-ce pas?
Jamornh
3
@Jamornh Dans votre exemple de pluie de météores, vous tirez un événement: pour chaque météore qui commence à tomber, pour chaque météore qui touche le sol, peut-être un pour les personnages qui ont du mal à lancer le sort. En utilisant cette solution, vous pouvez même modifier le nombre de météores et ne rencontrer aucun problème audio.
akaltar du
1
@Jamornh Vous pouvez également mettre en file d'attente un événement pour "lire le son d'explosion dans 30 images à partir de maintenant", la manière la plus simple de le faire est d'utiliser une fonction de rappel . Je vais détailler cela dans ma réponse.
bobobobo
1
@Jamornh L'animation ne doit pas nécessairement être modulaire pour pouvoir filmer plusieurs événements (à des moments prédéterminés). Dans votre éditeur d'effets, vous pouvez simplement dire, jouez le son du boom à l'image 32.
akaltar
1
Oui, ça marcherait. Si vous utilisiez JSON, je suggérerais une infrastructure de données comme { 'images':'sprite-%02d.png', 'beginRange':1, 'endRange':32, 'sounds':{ 0 : 'startSpell.wav', 30 : 'impact.wav' } }. Si votre jeu est à un stade très précoce et que le temps de compilation est encore faible, vous pouvez commencer par coder en dur les structures de données pour voir si cela fonctionne.
bobobobo
1

La façon dont je le fais est de créer des écouteurs d'événements personnalisés pour ma classe d'animation et de les faire contrôler mon son. donc si mon animation a commencé callback.start (); et commencer mon son dans cette méthode. si mon animation a été interrompue, faites callback.pause (); et mettre le son en pause. lorsque l'animation est terminée, vous callback.end (); et avoir la fin du son aussi.

mais pour une synchronisation parfaite, j'irais jusqu'à compter les images sonores et dormir (mettre en pause) mon son s'il arrive trop loin et faire de même pour mon animation.

Je n'ai jamais eu à le faire jusqu'à ce jour car la première suggestion répond très bien à mes besoins pour l'instant.

Jonathan Camarena
la source
Pourriez-vous nous expliquer comment compter un "cadre" sonore? Voulez-vous dire compter le nombre de millisecondes qui se sont écoulées depuis le début du son et effectuer un comptage pour chaque intervalle prédéfini?
Jamornh
non non, je veux dire littéralement le cadre sur lequel le son est activé. Je ne sais pas comment vous le faites dans cocos2D mais dans le son java, il existe des méthodes pour obtenir le nombre de sons et le "cadre" actuel, il existe également un moyen de définir le cadre actuel à un certain niveau. Je suis sûr que si vous y regardez, vous pouvez trouver des variables similaires dans votre interface audio, mais comme quelqu'un l'a souligné, il est préférable que vos mises à jour gèrent le temps d'animation, etc. afin que vous n'ayez pas à faire de tels hacks de synchronisation.
Jonathan Camarena