Qual è il modo standard per sincronizzare gli effetti sonori con le animazioni sprite?


8

Prendiamo una situazione in cui hai un gioco di ruolo con incantesimi e ogni animazione di incantesimi ha un numero diverso di fotogrammi e hanno requisiti molto diversi per gli effetti sonori. Supponiamo che ogni incantesimo abbia solo 1 animazione continua associata (al contrario di più pezzi modulari che sono usati per formare un'animazione completa) sui vecchi giochi di Final Fantasy a 16 bit.

L'unico modo in cui mi viene in mente per assicurarmi che i suoni e le animazioni si sincronizzino è:

  • Ottieni il numero di fotogrammi di un'animazione.
  • Prendi il tempo tra ogni fotogramma dell'animazione. (se è 30 fps, allora è 1/30 di secondo per fotogramma.)
  • Quindi crea un file audio della stessa lunghezza dell'animazione.

Ciò significa che se un'animazione dura 5 secondi, a 30 fps, con un totale di 150 fotogrammi, anche il file audio sarà lungo 5 secondi. Se l'animazione dovrebbe avere un suono di "impatto" sul 30 ° fotogramma, ciò significa che il file audio includerà il suono di impatto al segno di 1,0 secondi.

Alla fine, iniziamo l'animazione e l'effetto sonoro esattamente allo stesso tempo e speriamo che i frame e il suono si sincronizzino.

Sembra che ciò possa creare problemi quando i fotogrammi vengono saltati o succede qualcosa durante l'animazione e il suono viene riprodotto leggermente troppo presto o troppo tardi e causerà la disconnessione del suono e dell'animazione. È questo l'approccio migliore o di solito c'è un modo migliore che non vedo?

La risposta non deve necessariamente essere specifica per Cocos2D se è concettuale, ma se esiste una soluzione specifica per Cocos2d, mi piacerebbe ascoltarla.

EDIT: Mi rendo anche conto che con questo metodo, se ci capita di entrare e adattare il numero di fotogrammi o i tempi dell'animazione in seguito, DEVO anche tornare indietro e cambiare il file audio. Sembra una terribile causa di errore umano (dimenticando di aggiornare i file audio dopo il cambio dell'animazione). Spero che ci siano metodi migliori là fuori.


questo è il motivo per cui i cicli di gioco a tempo costante sono utili: quindi non devi preoccuparti che l'animazione vada fuori sincronia
maniaco del cricchetto

@ratchetfreak Credo che cocos2d gestirà correttamente i tempi dell'animazione. Se creo un'animazione e dico a cocos2d che voglio esattamente 1/30 di secondo tra i fotogrammi, ciò garantirà questo e salterà i fotogrammi se le prestazioni non sono abbastanza buone. Questo assicura che l'animazione sia completata al momento giusto (cioè a tempo costante). Detto questo, stai dicendo che il metodo che ho descritto sopra è la strada giusta da percorrere?
Jamornh,

Risposte:


6

Fallo tramite eventi.

L'inizio dell'incantesimo è un evento . Inizia a riprodurre l'audio per quell'evento.

Anche il nemico che viene colpito da un incantesimo è un evento. Se il nemico è più lontano e si lancia un dardo, ad esempio, si suona il secondo suono (colpire il dardo) solo quando il dardo raggiunge il bersaglio (se si considera il lancio come un incantesimo).

Se è necessario collegarlo a un fotogramma (ad esempio, riprodurre il suono "esplodere" da 30 fotogrammi da adesso indipendentemente dalle frequenze dei fotogrammi in tempo reale )), il modo più semplice per farlo è utilizzare i callback . I callback sono solo "blocchi di codice che programmerai di eseguire in futuro". Ecco un esempio del mio setter per la creazione di callback:

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

A TimedCallbackè solo un involucro attorno a un std::function(o potresti usare un Objective-C ^{block}. std::functionViene eseguito quando il suo frame è attivo.

Un altro modo (meno globale) è includere eventi nell'animazione . Quindi, se hai bisogno di riprodurre suoni specifici su specifici frame di animazione, allora archivia un map<int,Sound*>nella Animationclasse. Su ciascun fotogramma dell'animazione, controlla se esiste un corrispondente Sound*da riprodurre per quel fotogramma.

È inoltre possibile memorizzare un map<int, std::function>, Animationnell'oggetto, che consente di richiamare a functiondurante l'animazione.


Penso che potresti aver frainteso leggermente la domanda. Il tuo metodo funzionerebbe per suoni brevi pensati per il tipo "punch", "hit", "kick", "shoot" che dura non più di una frazione di secondo in cui la sincronizzazione non è un problema (puoi semplicemente riprodurre l'effetto sonoro "su evento "come hai suggerito.) Tuttavia con una lunga animazione + suono (ad es. armageddon, in cui 5-6 meteore cadono dal cielo e colpiscono il terreno in diversi frame, ma fanno parte di 1 animazione sprite [come il modo in cui la fantasia finale li fa] avrà solo 1 evento di inizio, non uno per meteorite) questo metodo non garantisce la sincronizzazione, giusto?
Jamornh,

3
@Jamornh Nel tuo esempio di sciame meteorico spari un evento: per ogni meteorite che inizia a cadere, poiché ogni meteorite che colpisce il terreno, forse uno per i personaggi che lottano per lanciare l'incantesimo. Usando questa soluzione puoi anche cambiare il numero di meteore e non avere problemi con l'audio.
Akaltar,

1
@Jamornh Potresti anche mettere in coda un evento per "riprodurre il suono dell'esplosione a 30 fotogrammi da ora", il modo più semplice per farlo è usare una funzione di callback . Lo descriverò in dettaglio nella mia risposta.
bobobobo,

1
@Jamornh L'animazione non deve necessariamente essere modulare per poter girare più eventi (a orari prestabiliti). Nel tuo editor di effetti potresti semplicemente dire,
ascolta il

1
Sì, funzionerebbe. Se steste usando JSON, suggerirei una struttura di dati come { 'images':'sprite-%02d.png', 'beginRange':1, 'endRange':32, 'sounds':{ 0 : 'startSpell.wav', 30 : 'impact.wav' } }. Se il tuo gioco è molto precoce e il tempo di compilazione è ancora basso, puoi iniziare codificando le strutture di dati per vedere se funziona.
bobobobo,

1

Il modo in cui lo faccio è creare ascoltatori di eventi personalizzati per la mia classe di animazione e farli controllare il mio suono. quindi se la mia animazione ha iniziato callback.start (); e inizia il mio suono con quel metodo. se la mia animazione è stata messa in pausa, fare callback.pause (); e metti in pausa il suono. quando l'animazione è terminata, callback.end (); e anche la fine del suono.

ma per una sincronizzazione perfetta andrei fino al conteggio dei fotogrammi sonori e al sonno (in pausa) del mio suono se arriva fino in fondo e fa lo stesso per la mia animazione.

Non ho mai dovuto farlo fino ad oggi perché il primo suggerimento soddisfa le mie esigenze proprio bene per ora.


Potresti approfondire come conteresti un "frame" sonoro? Intendi contare il numero di millisecondi trascorsi dall'avvio del suono ed eseguire un conteggio per ogni intervallo predefinito?
Jamornh,

no no, intendo letteralmente il fotogramma su cui si trova il suono. Non so come si fa in cocos2D ma nel suono Java ci sono metodi per ottenere il framecount dei suoni e l'attuale "frame", c'è anche un modo per impostare il frame corrente su un certo. sono sicuro che se lo guardi potresti trovare variabili simili all'interno della tua interfaccia audio, ma come qualcuno ha sottolineato, è meglio che gli aggiornamenti gestiscano il tempo di animazione ect. in modo che tu non abbia fatto questi hack di sincronizzazione.
Jonathan Camarena,
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.