サウンドエフェクトをスプライトアニメーションと同期させる標準的な方法は何ですか?


8

呪文付きのRPGがあり、各呪文アニメーションのフレーム数が異なり、サウンド効果に対する要件が非常に異なる状況を考えてみましょう。各呪文に関連付けられている連続したアニメーションは1つだけであると仮定しましょう(フルアニメーションを形成するために使用される複数のモジュラーピースとは対照的)、古い16ビットファイナルファンタジーゲーム。

サウンドとアニメーションを確実に同期させるために私が考えられる唯一の方法は、次のとおりです。

  • アニメーションのフレーム数を取得します。
  • アニメーションの各フレーム間の時間を取得します。(30 fpsの場合、フレームあたり1/30秒です。)
  • 次に、アニメーションと正確に同じ長さのサウンドファイルを作成します。

つまり、アニメーションが5秒で30 fps、合計150フレームの場合、サウンドファイルも5秒になります。アニメーションの30番目のフレームで「インパクト」サウンドが必要な場合、これは、サウンドファイルに1.0秒のマークでインパクトのサウンドが含まれることを意味します。

最後に、アニメーションと効果音を同時に開始し、フレームとサウンドが同期することを期待します。

これは、フレームがスキップされるか、アニメーション中に何かが発生し、サウンドの再生が少し早すぎる、または遅すぎる場合に問題が発生し、サウンドとアニメーションが同期しなくなる可能性があるようです。これは最善のアプローチですか、それとも私が見ないだけのより良い方法がありますか?

答えが概念的なものである場合は特に、Cocos2Dに対する答えである必要はありませんが、cocos2dに対する特定の解決策がある場合は、ぜひ聞いてください。

編集:この方法では、後でフレーム数やアニメーションのタイミングを調整して調整した場合、戻ってサウンドファイルを変更する必要があることにも気づきました。これは、人為的エラーの恐ろしい原因のように聞こえます(アニメーションの変更後にサウンドファイルを更新するのを忘れている)。


これが、一定時間のゲームループが便利な理由です。その場合、アニメーションが同期されなくなることを心配する必要はありません
ラチェットフリーク

@ratchetfreak cocos2dがアニメーションのタイミングを正しく管理できると思います。アニメーションを作成してcocos2dに、フレーム間の正確に1/30秒が必要であることを伝えると、パフォーマンスが十分でない場合、フレームがスキップされ、フレームがスキップされます。これにより、アニメーションが適切なタイミングで(つまり、一定の時間で)完了することが保証されます。以上を踏まえると、上で概説した方法が正しい方法であると言っていますか?
Jamornh 2013年

回答:


6

イベントを介してそれを行います。

スペル開始はイベントです。そのイベントのサウンドの再生を開始します。

呪文に襲われる敵もイベントです。たとえば、敵がさらに遠くにいて、ダーツを投げた場合、ダーツがターゲットに到達すると、2番目のサウンド(ダーツヒット)のみが再生されます(スローを呪文と見なす場合)。

フレームに関連付ける必要がある場合(たとえば、リアルタイムフレームレートに関係なく、 30フレーム後に「爆発」サウンドを再生する場合)、これを行う最も簡単な方法は、コールバックを使用することです。コールバックは、単に「将来実行するようにスケジュールしたコードのブロック」です。これが私のコールバック作成セッターの例です。

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

A TimedCallbackはのラッパーにすぎstd::functionません(またはObjective-Cを使用できます^{block}std::functionフレームがアップすると、が実行されます。

別の方法(あまりグローバルではない)は、アニメーションイベントを含めることです。したがって、特定のアニメーションフレームで特定のサウンドを何度も再生する必要がある場合map<int,Sound*>は、Animationクラスにを格納します。アニメーションの各フレームで、Sound*そのフレームの再生に対応するものがあるかどうかを確認します。

オブジェクトにを格納してmap<int, std::function>、アニメーション中にAnimationをコールバックすることもできますfunction


少し誤解されていると思います。あなたの方法は、「パンチ」、「ヒット」、「キック」、「シュート」タイプを意味する短いサウンドで機能し、同期が問題とならない場合、1秒未満しか持続しません(サウンド効果は「オン」と言います。ただし、長いアニメーション+サウンド(つまり、ハルマゲドン、5-6の流星が空から落ちてさまざまなフレームで地面にぶつかりますが、1つのスプライトアニメーションの一部です[ファイナルファンタジーのように])開始イベントは1つだけで、隕石ごとに1つはありません)この方法では同期が正しく行われませんか?
Jamornh、2013

3
@Jamornh流星群の例では、イベントを撮影します。流星が落下し始めるたびに、地面に衝突する流星ごとに、おそらく、呪文のキャストに苦労しているキャラクターのために1つです。このソリューションを使用すると、流星の数を変更することもでき、オーディオに問題はありません。
akaltar 2013

1
@Jamornh「今から30フレーム後に爆発音を再生する」ためにイベントをキューに入れることもできます。これを行う最も簡単な方法は、コールバック関数を使用することです。これについては私の回答で詳しく説明します。
bobobobo 2013

1
@Jamornh複数のイベントを(所定の時間に)撮影できるようにするために、アニメーションは必ずしもモジュール式である必要はありません。あなたの効果エディタであなただけのフレーム32でブームサウンドを再生する、と言うことができる
akaltar

1
はい、それでうまくいきます。JSONを使用している場合は、のようなデータ構造をお勧めし{ 'images':'sprite-%02d.png', 'beginRange':1, 'endRange':32, 'sounds':{ 0 : 'startSpell.wav', 30 : 'impact.wav' } }ます。ゲームが非常に初期の段階で、コンパイル時間がまだ短い場合は、データ構造をハードコーディングして動作するかどうかを確認することから始めます。
bobobobo 2013

1

私のやり方は、アニメーションクラスのカスタムイベントリスナーを作成し、それらに私のサウンドを制御させることです。したがって、アニメーションがcallback.start();を開始した場合。その方法で私のサウンドを開始します。アニメーションが一時停止された場合は、callback.pause();を実行します。と音を一時停止します。アニメーションが終了すると、callback.end(); 音も終わります。

しかし、完璧な同期のためには、サウンドフレームを数えて、サウンドが眠っている(一時停止している)限り、頭まで届き、アニメーションでも同じことを行います。

最初の提案は今のところ私のニーズをうまく満たしているので、私はこの日までそれをする必要がありませんでした。


サウンドの「フレーム」を数える方法について詳しく教えてください。サウンドが開始してから経過したミリ秒数をカウントし、事前定義された間隔ごとにカウントを実行することを意味しますか?
Jamornh、2013

いいえ、私は文字通り、サウンドが存在するフレームを意味します。私はcocos2Dでそれを行う方法を知りませんが、Javaサウンドには、サウンドのフレームカウントと現在の「フレーム」を取得するためのメソッドがあり、現在のフレームを特定に設定する方法もあります。調べてみると、サウンドインターフェース内に同様の変数が見つかるかもしれませんが、誰かが指摘したように、更新でアニメーション時間などを処理するのが最善です。あなたがそのような同期ハックをする必要がないように。
ジョナサンカマレナ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.