Javaアプレットのアニメーション

この記事では、JavaアプレットAPIを使用してアニメーションを実装する方法について説明します。一般的に使用される手法について説明し、各手法を説明する簡単な例を示します。

基本的なアニメーションテクニック

Javaではさまざまな形式のアニメーションが可能です。それらすべてに共通しているのは、比較的高速(通常は1秒間に約10〜20回)で連続するフレームを描画することにより、画面上にある種のモーションを作成することです。

アニメーションを実行するための単純なテンプレートアプレットを作成することから始め、かなり完全なアプレットに到達するまでゆっくりと詳しく説明します。

スレッドを使用する

1秒間に複数回画面を更新するには、アニメーションループを含む新しいJavaスレッドを作成する必要があります。アニメーションループは、現在のフレームを追跡し、定期的な画面更新を要求する役割を果たします。スレッドを実装するThreadには、Runnableインターフェイスのサブクラスを作成するか、インターフェイスに準拠する必要があります。

よくある間違いは、アニメーションループをpaint()アプレットのメソッドに配置することです。これを行うと、すべての描画とイベント処理を担当するメインAWTスレッドが保持されるため、奇妙な副作用が発生します。

例として、アニメーションアプレットの一般的な概要を示すExample1Appletという小さなテンプレートアプレットを作成しました。Example1Appletは、スレッドを作成し、repaint()一定の間隔でメソッドを呼び出す方法を示しています。1秒あたりのフレーム数は、アプレットパラメータを渡すことで指定されます。HTMLドキュメントに入れるものの例を次に示します。


  

これがExample1Appletです。

注意:

このアプレットは、実際にはまだ画面に何も描画していません。画面への描画については後で説明します。また、ユーザーがページを離れるたびにアプレットがアニメーションスレッドを破棄することにも注意してください(これにより、アプレットのstop()メソッドが呼び出されます)。これにより、アプレットのページが表示されていないときに、アプレットがCPU時間を浪費することがなくなります。

一定のフレームレートを維持する

上記の例では、アプレットはフレーム間で一定時間スリープするだけです。これには、待つ時間が長すぎるという欠点があります。スレッドを実行するだけで時間が失われるため、1秒あたり10フレームを取得するには、フレーム間で100ミリ秒待つ必要はありません。

次のアプレット、Example2Appletは、より良い時間を維持する方法を示しています。開始時間を追跡することにより、フレーム間の正しい遅延を計算するだけです。現在の時刻に基づいて、フレーム間の推定必要遅延を計算します。

これがExample2Appletです。

各フレームのペイント

残っているのは、各フレームをペイントすることです。前の例ではrepaint()、各フレームを呼び出します。これにより、アプレットのpaint()メソッドが呼び出されます。Example3Appletにはpaint()、現在のフレームの番号を画面に描画するメソッドがあります。

これがExample3Appletの動作であり、その後にコードリストが続きます。

注意:

フレームレートを非常に高く指定すると(たとえば、1秒あたり100フレーム)、run()メソッドはrepaint()1秒あたり100回呼び出します。ただし、paint()再描画リクエストの発行が速すぎると、1つの画面の更新に折りたたまれるため、これによって1秒あたり100回の呼び出しが発生するとは限りません。これが、run()メソッドではなくメソッドで現在のフレーム番号を追跡する理由paint()です。

グラフィックの生成

それでは、少し描きにくいものをアニメートしましょう。Example4Appletは、正弦波の組み合わせを描画します。x座標ごとに、短い垂直線を描画します。これらの線はすべて一緒になって、フレームごとに変化する単純なグラフを形成します。残念ながら、このアプローチでは多くの点滅が発生することがわかります。点滅の原因といくつかの対策については、次のセクションで説明します。

これがExample4Appletの動作であり、その後にコードリストが続きます。

過度の点滅を避ける

Example4Appletに表示される点滅には、2つの原因があります。各フレームのペイントに時間がかかりすぎる(再ペイント中に必要な計算量が多いため)、およびpaint()呼び出される前に背景全体がクリアされる。次のフレームの計算が行われている間、ユーザーにはアニメーションの背景が表示されます。

背景がクリアされてから正弦波がペイントされるまでのこの短い時間は、フラッシュとして表示されます。PCなどの一部のプラットフォームでは、XWindowsよりも点滅が目立ちます。その理由は、X Windowsグラフィックがバッファリングされているため、フラッシュが少し短くなるためです。

update()メソッドの実装とダブルバッファリングの使用(バックバッファの使用とも呼ばれますという2つの簡単なトリックを使用して、フラッシュを大幅に減らすことができます。

update()メソッドのオーバーライド

AWTは、アプレットの再描画要求を受信すると、アプレットのupdate()メソッドを呼び出します。デフォルトでは、update()メソッドはアプレットの背景をクリアしてからメソッドを呼び出しますpaint()update()メソッドをオーバーライドして、メソッドに含まれていた描画コードを含めることにより、paint()再描画のたびにアプレットの領域全体がクリアされるのを回避できます。

背景が自動的にクリアされなくなったので、update()メソッドで自分でクリアする必要があります。新しい線を描画する前に、グラフの各垂直線を個別に消去できるようになり、点滅が完全になくなりました。この効果はExample5Appletに示されています。

これがExample5Appletの動作であり、その後にコードリストが続きます。

注意:

update()メソッドをオーバーライドするときはいつでも、を実装する必要がありますpaint()。これはpaint()、アプレットの描画領域に「損傷」が発生するたびに、たとえば、アプレットの描画領域の一部を覆い隠しているウィンドウが画面から削除されたときに、メソッドがAWT描画システムによって直接呼び出されるためです。あなたのpaint()実装では、単純に呼び出すことができますupdate()

ダブルバッファリング

フレーム間の点滅を減らす別の方法は、ダブルバッファリングを使用することです。この手法は、多くのアニメーションアプレットで使用されています。

一般的な原則は、オフスクリーンイメージを作成し、イメージにフレームを描画してから、を1回呼び出すだけでイメージ全体をスクリーンに叩きつけることですdrawImage()。利点は、ほとんどの描画が画面外で行われることです。画面外の画像を画面に最終的にペイントする方が、通常、フレームを画面に直接ペイントするよりもはるかに効率的です。

二重バッファリングを使用した正弦波アプレットをExample6Appletに示します。アニメーションが非常にスムーズで、フレームを描画するときに特別なトリックは必要ないことがわかります。唯一の欠点は、描画領域と同じ大きさのオフスクリーン画像を割り当てる必要があることです。描画領域が非常に大きい場合、これにはかなりのメモリが必要になる場合があります。

これがExample6Appletの動作であり、その後にコードリストが続きます。

注意:

ダブルバッファリングを使用するupdate()場合は、フレームをペイントする前にアプレットの背景をクリアしたくないため、メソッドをオーバーライドする必要があります。(オフスクリーン画像に描画して、自分で背景をクリアします。)

画像の使用

次に、paintFrame()いくつかの画像をアニメーション化するメソッドでメソッドを書き直します。これにより、問題にいくつかの小さな問題が追加されます。画像はかなり大きく、段階的に読み込まれます。特に低速の接続で画像をロードする場合は、画像が完全に描画されるまでに長い時間がかかることがあります。これが、drawImage()メソッドが4番目の引数であるImageObserverオブジェクトを受け取る理由です。画像オブザーバーは、より多くの画像データが到着したときに通知されるオブジェクトです。画像を取得するには、このgetImage()メソッドを使用します。

画面上で画像を移動する

この最初の画像アニメーションアプレットExample7Appletは、次の2つの画像を使用します。

world.gif:car.gif:

背景には世界の画像を使用し、その上に車の画像を2回描画して、世界中を走る2台の車のアニメーションを作成します。

これがExample7Appletの動作であり、その後にコードリストが続きます。

一連の画像を表示する

Example8Appletは、フレームごとに個別の画像を使用してアニメーションを作成する方法を示しています。使用されている10個のフレームは次のとおりです。

T1.gif:T2.gif:T3.gif:T4.gif:T5.gif:

T6.gif:

T7.gif:

T8.gif:

T9.gif:

T10.gif:

フラッシュを排除するために、引き続きダブルバッファリングを使用しています。その理由は、レンダリングする各画像が部分的に透明であるため、次のフレームを描画する前に各フレームを消去する必要があるためです。これにより、ダブルバッファリングなしでフラッシュが発生します。

これがExample8Appletの動作であり、その後にコードリストが続きます。

注意:

画像のシーケンスを表示するときは、画像を正しく配置するように注意する必要があります。最も簡単な方法は、画像がすべて同じサイズで、同じ位置に描画できることを確認することです。そうでない場合、アプレットは各フレームを異なるオフセットで描画する必要があります。

MediaTrackerを使用して増分表示を回避する

Javaプログラムが画像をロードすると、画像が完全にロードされる前に画像を表示できます。ユーザーは、画像が最初に不完全にレンダリングされ、次に画像がロードされるにつれて徐々に完全にレンダリングされるのを確認します。このインクリメンタル表示は、ユーザーにフィードバックを提供し(知覚されるパフォーマンスを向上させます)、画像の読み込み中にプログラムが他のタスクを簡単に実行できるようにします。

アニメーションに関しては、インクリメンタル画像表示は背景画像には役立ちますが、アニメーション画像に使用すると非常に気が散ることがあります。したがって、アニメーション全体がロードされるまで待ってから表示することが望ましい場合があります。

Jim GrahamのMediaTrackerクラスを使用して、画像のダウンロードを追跡し、画像のセット全体が完全にダウンロードされるまでアニメーションの表示を遅らせることができます。Example9Appletは、MediaTrackerクラスを使用して、手を振っているデュークアニメーションの画像をダウンロードする方法を示しています。

これがExample9Appletの動作であり、その後にコードリストが続きます。

音を追加する

アニメーションにサウンドを追加するのは簡単です。このgetAudioClip()メソッドを使用して、AudioClipオブジェクトを取得できます。後で、クリップを連続ループまたは単一のサウンドとして再生できます。Example10Appletは、アニメーション中に連続的な背景音と繰り返し音を再生する方法を示しています。

これがExample10Appletの動作であり、その後にコードリストが続きます。

注意:

連続音を再生するときは、ユーザーがページを離れるときに停止することを忘れないでください(つまり、アプレットのstop()方法で停止します)。

別の注意:

連続オーディオは非常に煩わしい場合があります。ページを離れずにオーディオをオフにする方法をユーザーに提供することをお勧めします。ボタンを提供するか、ユーザーがアプレットをクリックしたときにオーディオをオフにすることができます。

画像をより速く読み込むためのヒント

多くの画像を使用するアニメーションは、ダウンロードに時間がかかります。これは主に、すべての画像ファイルに対して新しいHTTP接続が確立されるためであり、十分な帯域幅がある場合でも、接続には数秒かかる場合があります。

このセクションでは、アプレットが画像のダウンロードを高速化するために使用できる2つの画像形式について説明します。

イメージストリップの使用

複数のアニメーションフレームを含む単一の画像を使用することで、ダウンロードのパフォーマンスを向上させることができます。clipRect()演算子を使用して、画像から1つのフレームをレンダリングできます。以下は、UnderConstructionアプレットで使用される画像ストリップの例です。

アプレットは、前のフレームを消去しないことでドリル効果を作成します。背景はたまにしかクリアされません。

これが実際のUnderConstructionであり、そのソースコードへのリンクがあります。

Flicを使用したフレーム間圧縮

複数のフレームで構成されるアニメーションのダウンロードパフォーマンスを本当に向上させたい場合は、何らかの形式のフレーム間圧縮を使用する必要があります。

アニメーションツール

現時点(1996年1月)では、Javaを利用したアニメーションの作成に役立つツールはほとんどありません。私が見つけた最高のツールは、DimensionXのThe Easy Animator(TEA)(以前はJAMと呼ばれていました)です。インタラクティブにアニメーションを作成できます。開発者には、Javaでアニメーションを作成するためのツールをもっと作成することをお勧めします。

表示する既製の画像がいくつかある場合は、Animatorアプレットを使用できます。Animatorには、連続サウンド、フレーム固有のサウンド、個々のフレームのタイミングと位置、起動イメージ、フレームの順序などを指定できる多くのパラメーターがあります。

また、ガムランアニメーションのページをチェックして、アニメーションを使用する多くのアプレットを見つける必要があります。

結論

この記事が、アプレット開発者がより多くのより良いアニメーションアプレットを作成するのに役立つことを願っています。また、より良いツールがすぐに利用できるようになることを願っています。

Arthur van Hoffは、最近までSun Microsystemsのシニアスタッフエンジニアであり、1993年からJava言語の開発に携わっています。彼は完全にJavaで記述された最初のJavaコンパイラの著者です。彼は最近Sunを去り、Sami Shaio、Kim Polese、JonathanPayneと一緒に新しい会社を設立しました。新会社はJavaアプリケーションの構築に焦点を合わせます。 Kathy Walrathは、SunMicrosystemsのテクニカルライターです。彼女は1993年からJavaチームの一員です。現在、彼女はMary Campioneと協力して、Javaチュートリアル:インターネット用のオブジェクト指向プログラミング、Java言語、アプレットプログラミング、およびJavaGUIプログラミングを学習するためのアプレット拡張チュートリアルに取り組んでいます。 。オンラインで利用できることに加えて、Javaチュートリアルは、Addison-WesleyJavaシリーズの一部として今年の夏にも公開されます。

このストーリー「Javaアプレットのアニメーション」は、もともとJavaWorldによって公開されました。