Animation in Java-Applets

In diesem Artikel wird beschrieben, wie Sie Animationen mithilfe der Java-Applet-API implementieren. Es beschreibt häufig verwendete Techniken und gibt ein einfaches Beispiel zur Veranschaulichung jeder Technik.

Grundlegende Animationstechniken

In Java sind viele Arten von Animationen möglich. Allen gemeinsam ist, dass sie eine Art Bewegung auf dem Bildschirm erzeugen, indem sie aufeinanderfolgende Bilder mit einer relativ hohen Geschwindigkeit zeichnen (normalerweise etwa 10 bis 20 Mal pro Sekunde).

Wir beginnen mit der Erstellung eines einfachen Vorlagen-Applets zum Erstellen von Animationen und arbeiten es langsam aus, bis wir zu einem ziemlich vollständigen Applet gelangen.

Mit einem Thread

Um den Bildschirm mehrmals pro Sekunde zu aktualisieren, müssen Sie einen neuen Java-Thread erstellen, der eine Animationsschleife enthält. Die Animationsschleife ist dafür verantwortlich, den aktuellen Frame zu verfolgen und regelmäßige Bildschirmaktualisierungen anzufordern. Um einen Thread zu implementieren, müssen Sie entweder eine Unterklasse von erstellen Threadoder sich an die RunnableSchnittstelle halten.

Ein häufiger Fehler besteht darin, die Animationsschleife in die paint()Methode eines Applets einzufügen. Dies hat seltsame Nebenwirkungen, da es den Haupt-AWT-Thread hält, der für das gesamte Zeichnen und die Ereignisbehandlung verantwortlich ist.

Als Beispiel habe ich ein kleines Vorlagen-Applet namens Example1Applet geschrieben, das den allgemeinen Umriss eines Animations-Applets veranschaulicht. Beispiel1Applet zeigt, wie Sie einen Thread erstellen und die repaint()Methode in festen Intervallen aufrufen . Die Anzahl der Bilder pro Sekunde wird durch Übergabe eines Applet-Parameters angegeben. Hier ist ein Beispiel dafür, was Sie in Ihr HTML-Dokument einfügen würden:


  

Hier ist Example1Applet.

Hinweis:

Dieses Applet zeichnet noch nichts auf den Bildschirm. Das Zeichnen auf dem Bildschirm wird später erklärt. Beachten Sie auch, dass das Applet seinen Animationsthread zerstört, wenn der Benutzer die Seite verlässt (was dazu führt, dass die stop()Methode des Applets aufgerufen wird). Dadurch wird sichergestellt, dass das Applet keine CPU-Zeit verschwendet, solange seine Seite nicht sichtbar ist.

Konstante Bildrate beibehalten

Im obigen Beispiel schläft das Applet einfach für eine feste Zeit zwischen Frames. Dies hat den Nachteil, dass Sie manchmal zu lange warten. Um 10 Bilder pro Sekunde zu erhalten, sollten Sie nicht 100 Millisekunden zwischen den Bildern warten, da Sie einige Zeit verlieren, wenn Sie nur den Thread ausführen.

Das folgende Applet, Example2Applet, zeigt, wie Sie eine bessere Zeit erhalten. Es berechnet einfach die korrekte Verzögerung zwischen Frames, indem die Startzeit verfolgt wird. Es berechnet die geschätzte erforderliche Verzögerung zwischen Frames basierend auf der aktuellen Zeit.

Hier ist Example2Applet.

Malen Sie jeden Rahmen

Was bleibt, ist jeden Rahmen zu malen. In den vorherigen Beispielen rufen wir repaint()für jeden Frame auf, wodurch die Applet- paint()Methode aufgerufen wird. Das Example3Applet verfügt über eine paint()Methode, mit der die Nummer des aktuellen Frames auf dem Bildschirm angezeigt wird.

Hier ist Example3Applet in Aktion, gefolgt von einer Codeliste.

Hinweis:

Wenn Sie die Bildrate als sehr hoch angeben (z. B. 100 Bilder pro Sekunde), run()ruft die Methode repaint()100 Mal pro Sekunde auf. Dies führt jedoch nicht immer zu 100 Aufrufen paint()pro Sekunde, da diese bei einer zu schnellen Repaint-Anforderung zu einem einzigen Bildschirm-Update zusammengefasst werden. Aus diesem Grund verfolgen wir die aktuelle Frame-Nummer in der run()Methode und nicht in der paint()Methode.

Grafiken generieren

Lassen Sie uns nun etwas animieren, das etwas schwieriger zu zeichnen ist. Das Example4Applet zeichnet eine Kombination von Sinuswellen. Für jede x-Koordinate wird eine kurze vertikale Linie gezeichnet. Alle diese Linien bilden zusammen ein einfaches Diagramm, das sich für jeden Frame ändert. Leider werden Sie feststellen, dass dieser Ansatz viel Blinken verursacht. Wir werden die Ursache des Blinkens und einige Abhilfemaßnahmen im nächsten Abschnitt erklären.

Hier ist Example4Applet in Aktion, gefolgt von einer Codeliste.

Übermäßiges Blinken vermeiden

Das in Example4Applet angezeigte Blinken hat zwei Ursachen: Das Malen jedes Frames dauert zu lange (aufgrund des Rechenaufwands, der während des Repaint erforderlich ist), und der gesamte Hintergrund wird gelöscht, bevor er paint()aufgerufen wird. Während die Berechnung des nächsten Frames ausgeführt wird, sieht der Benutzer den Hintergrund der Animation.

Diese kurze Zeit zwischen dem Löschen des Hintergrunds und dem Malen der Sinuswelle wird als Blitz gesehen. Auf einigen Plattformen wie dem PC ist das Blinken offensichtlicher als unter X Windows. Der Grund ist, dass die X Windows-Grafiken gepuffert sind, wodurch der Flash etwas kürzer wird.

Sie können das Flashen mit zwei einfachen Tricks erheblich reduzieren: Implementieren der update()Methode und Verwenden der doppelten Pufferung (manchmal auch als Backbuffer bezeichnet ).

Überschreiben der update () -Methode

Wenn der AWT eine Repaint-Anforderung für ein Applet erhält, ruft er die Applet- update()Methode auf. Standardmäßig update()löscht die Methode den Hintergrund des Applets und ruft dann die paint()Methode auf. Durch Überschreiben der update()Methode, um den Zeichnungscode aufzunehmen, der früher in der paint()Methode enthalten war, wird vermieden, dass der gesamte Bereich des Applets bei jedem Repaint gelöscht wird.

Nachdem der Hintergrund nicht mehr automatisch gelöscht wird, müssen wir dies in der update()Methode selbst tun . Wir können jetzt jede vertikale Linie des Diagramms einzeln löschen, bevor wir die neue Linie zeichnen, wodurch das Blinken vollständig beseitigt wird. Dieser Effekt wird in Example5Applet gezeigt.

Hier ist Example5Applet in Aktion, gefolgt von einer Codeliste.

Hinweis:

Wann immer Sie die update()Methode überschreiben , müssen Sie sie noch implementieren paint(). Dies liegt daran, dass die paint()Methode direkt vom AWT-Zeichnungssystem aufgerufen wird, wenn der Zeichenbereich des Applets "beschädigt" wird - beispielsweise wenn ein Fenster, das einen Teil des Zeichenbereichs des Applets verdeckt, vom Bildschirm entfernt wird. Ihre paint()Implementierung kann einfach aufrufen update().

Doppelte Pufferung

Eine andere Möglichkeit, das Blinken zwischen Frames zu reduzieren, ist die doppelte Pufferung. Diese Technik wird in vielen Animations-Applets verwendet.

Das allgemeine Prinzip ist, dass Sie ein Offscreen-Bild erstellen, einen Rahmen in das Bild zeichnen und dann das gesamte Bild mit einem Aufruf von auf den Bildschirm klopfen drawImage(). Der Vorteil ist, dass der größte Teil der Zeichnung außerhalb des Bildschirms erfolgt. Das endgültige Malen des Offscreen-Bildes auf den Bildschirm ist normalerweise viel effizienter als das Malen des Rahmens direkt auf den Bildschirm.

Das Sinuswellen-Applet mit doppelter Pufferung ist in Beispiel 6Applet dargestellt. Sie werden sehen, dass die Animation ziemlich flüssig ist und Sie beim Zeichnen des Rahmens keine besonderen Tricks benötigen. Der einzige Nachteil ist, dass Sie ein Offscreen-Bild zuweisen müssen, das so groß ist wie der Zeichenbereich. Wenn der Zeichenbereich sehr groß ist, kann dies viel Speicherplatz erfordern.

Hier ist Example6Applet in Aktion, gefolgt von einer Codeliste.

Hinweis:

Wenn Sie die doppelte Pufferung verwenden, müssen Sie die update()Methode überschreiben , da der Hintergrund des Applets vor dem Malen des Rahmens nicht gelöscht werden soll. (Sie löschen den Hintergrund selbst, indem Sie auf das Offscreen-Bild zeichnen.)

Bilder verwenden

Jetzt schreiben wir die paintFrame()Methode mit einer Methode um, die einige Bilder animiert. Dies fügt dem Problem einige kleinere Komplikationen hinzu. Die Bilder sind ziemlich groß und werden schrittweise geladen. Es kann lange dauern, bis Bilder vollständig gezeichnet sind, insbesondere wenn Sie sie über eine langsame Verbindung laden. Dies ist der Grund, warum die drawImage()Methode ein viertes Argument verwendet, ein ImageObserver-Objekt. Der Bildbeobachter ist ein Objekt, das benachrichtigt wird, wenn mehr Bilddaten eingetroffen sind. Um die Bilder zu erhalten, verwenden wir die getImage()Methode.

Verschieben eines Bildes über den Bildschirm

Dieses erste bildanimierende Applet, Example7Applet, verwendet die folgenden zwei Bilder:

world.gif: car.gif:

Das Weltbild wird als Hintergrund verwendet, und das Autobild wird zweimal darüber gezeichnet, wodurch eine Animation von zwei Autos erstellt wird, die um die Welt rasen.

Hier ist Example7Applet in Aktion, gefolgt von einer Codeliste.

Anzeigen einer Folge von Bildern

Beispiel 8Applet zeigt, wie eine Animation mit separaten Bildern für jeden Frame erstellt wird. Hier sind die 10 Frames, die verwendet werden:

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

T6.gif:

T7.gif:

T8.gif:

T9.gif:

T10.gif:

Wir verwenden immer noch doppelte Pufferung, um das Flashen zu vermeiden. Der Grund dafür ist, dass jedes Bild, das wir rendern, teilweise transparent ist. Daher müssen wir jeden Frame löschen, bevor wir den nächsten zeichnen. Dies würde ein Flashen ohne doppelte Pufferung verursachen.

Hier ist Example8Applet in Aktion, gefolgt von einer Codeliste.

Hinweis:

Bei der Anzeige von Bildsequenzen müssen Sie darauf achten, die Bilder korrekt auszurichten. Am einfachsten ist es, sicherzustellen, dass alle Bilder dieselbe Größe haben und an derselben Position gezeichnet werden können. Ist dies nicht der Fall, muss Ihr Applet jeden Frame mit einem anderen Versatz zeichnen.

Verwenden von MediaTracker, um eine inkrementelle Anzeige zu vermeiden

Wenn ein Java-Programm ein Bild lädt, kann es das Bild anzeigen, bevor das Bild vollständig geladen ist. Der Benutzer sieht, dass das Bild zuerst unvollständig und dann inkrementell immer vollständiger gerendert wird, wenn das Bild geladen wird. Diese inkrementelle Anzeige gibt dem Benutzer Feedback (Verbesserung der wahrgenommenen Leistung) und ermöglicht es dem Programm, andere Aufgaben einfach auszuführen, während das Bild geladen wird.

Wenn es um Animation geht, kann die inkrementelle Bildanzeige für Hintergrundbilder nützlich sein, sie kann jedoch sehr ablenkend sein, wenn sie für die animierten Bilder verwendet wird. Es ist daher manchmal wünschenswert zu warten, bis die gesamte Animation geladen ist, bevor sie angezeigt wird.

Sie können die MediaTrackerKlasse von Jim Graham verwenden , um das Herunterladen von Bildern zu verfolgen und die Animationsanzeige zu verzögern, bis der gesamte Satz von Bildern vollständig heruntergeladen ist. Beispiel9Applet zeigt, wie Sie mit der MediaTrackerKlasse Bilder für die winkende Duke-Animation herunterladen.

Hier ist Example9Applet in Aktion, gefolgt von einer Codeliste.

Sound hinzufügen

Es ist einfach, einer Animation Sound hinzuzufügen. Mit dieser getAudioClip()Methode können Sie ein AudioClip-Objekt abrufen. Später können Sie den Clip entweder als Endlosschleife oder als Einzelton abspielen. Beispiel 10Applet zeigt, wie ein kontinuierlicher Hintergrundton sowie ein sich wiederholender Ton während der Animation abgespielt werden.

Hier ist Example10Applet in Aktion, gefolgt von einer Codeliste.

Hinweis:

Wenn Sie einen Dauerton abspielen, müssen Sie daran denken, ihn anzuhalten, wenn der Benutzer die Seite verlässt (dh in der stop()Methode Ihres Applets ).

Noch ein Hinweis:

Kontinuierliches Audio kann sehr ärgerlich sein. Es ist eine gute Idee, dem Benutzer die Möglichkeit zu geben, das Audio auszuschalten, ohne die Seite zu verlassen. Sie können eine Schaltfläche bereitstellen oder einfach den Ton ausschalten, wenn der Benutzer auf das Applet klickt.

Tipps zum schnelleren Laden von Bildern

Das Herunterladen einer Animation, die viele Bilder verwendet, dauert lange. Dies ist hauptsächlich auf die Tatsache zurückzuführen, dass für jede Image-Datei eine neue HTTP-Verbindung hergestellt wird und das Herstellen einer Verbindung einige Sekunden dauern kann, selbst wenn genügend Bandbreite vorhanden ist.

In diesem Abschnitt werden zwei Bildformate beschrieben, mit denen Ihr Applet das Herunterladen von Bildern beschleunigen kann.

Verwenden eines Bildstreifens

Sie können die Download-Leistung verbessern, indem Sie ein einzelnes Bild verwenden, das mehrere Animationsrahmen enthält. Mit dem clipRect()Operator können Sie einen einzelnen Frame aus dem Bild rendern . Unten finden Sie ein Beispiel für einen Bildstreifen, der im Applet UnderConstruction verwendet wird.

Das Applet erzeugt einen Bohreffekt, indem die vorherigen Frames nicht gelöscht werden. Der Hintergrund wird nur ab und zu gelöscht.

Hier ist UnderConstruction in Aktion mit einem Link zum Quellcode.

Interframe-Komprimierung mit Flic

Wenn Sie die Download-Leistung einer Animation, die aus mehreren Frames besteht, wirklich verbessern möchten, müssen Sie eine Form der Interframe-Komprimierung verwenden.

Animationswerkzeuge

Derzeit (Januar 1996) stehen nur wenige Tools zur Verfügung, mit denen Sie Java-basierte Animationen erstellen können. Das beste Tool, das ich finden konnte, ist DimensionXs The Easy Animator (TEA) (früher bekannt als JAM). Sie können damit interaktiv Animationen erstellen. Wir möchten Entwickler ermutigen, mehr Tools zum Erstellen von Animationen in Java zu schreiben.

Wenn Sie einige vorgefertigte Bilder anzeigen möchten, können Sie das Animator-Applet verwenden. Animator verfügt über viele Parameter, mit denen Sie kontinuierliche Sounds, rahmenspezifische Sounds, individuelle Frame-Timings und -Positionen, ein Startbild, Frame-Reihenfolge usw. angeben können.

Sie sollten auch die Gamelan-Animationsseite besuchen, um viele Applets zu finden, die Animationen verwenden.

Fazit

Ich hoffe, dieser Artikel wird Applet-Entwicklern helfen, mehr und bessere Animations-Applets zu schreiben. Ich hoffe auch, dass bald bessere Tools verfügbar sein werden.

Arthur van Hoff war bis vor kurzem leitender Ingenieur bei Sun Microsystems und ist seit 1993 an der Entwicklung der Java-Sprache beteiligt. Er ist Autor des ersten vollständig in Java geschriebenen Java-Compilers. Kürzlich verließ er Sun, um gemeinsam mit Sami Shaio, Kim Polese und Jonathan Payne eine neue Firma zu gründen. Das neue Unternehmen wird sich auf die Erstellung von Java-Anwendungen konzentrieren. Kathy Walrath ist technische Redakteurin bei Sun Microsystems. Sie ist seit 1993 Teil des Java-Teams. Derzeit arbeitet sie mit Mary Campione an The Java Tutorial: Objektorientierte Programmierung für das Internet, einem Applet-erweiterten Tutorial zum Erlernen der Java-Sprache, Applet-Programmierung und Java-GUI-Programmierung . Das Java-Tutorial wird nicht nur online verfügbar sein, sondern auch in diesem Sommer als Teil der Addison-Wesley-Java-Reihe veröffentlicht.

Diese Geschichte "Animation in Java-Applets" wurde ursprünglich von JavaWorld veröffentlicht.