Animation dans les applets Java

Cet article décrit comment implémenter une animation à l'aide de l'API d'applet Java. Il décrit les techniques couramment utilisées et donne un exemple simple pour illustrer chaque technique.

Techniques d'animation de base

De nombreuses formes d'animation sont possibles en Java. Ce qu'ils ont tous en commun, c'est qu'ils créent une sorte de mouvement sur l'écran en dessinant des images successives à une vitesse relativement élevée (généralement environ 10 à 20 fois par seconde).

Nous commencerons par créer une applet de modèle simple pour faire des animations et nous l'élaborerons lentement jusqu'à ce que nous arrivions à une applet assez complète.

Utiliser un fil

Pour mettre à jour l'écran plusieurs fois par seconde, vous devez créer un nouveau thread Java contenant une boucle d'animation. La boucle d'animation est chargée de garder une trace de l'image actuelle et de demander des mises à jour d'écran périodiques. Pour implémenter un thread, vous devez créer une sous-classe Threadou adhérer à l' Runnableinterface.

Une erreur courante est de mettre la boucle d'animation dans la paint()méthode d'une applet. Cela aura des effets secondaires étranges car cela retarde le thread principal AWT, qui est en charge de tous les dessins et de la gestion des événements.

À titre d'exemple, j'ai écrit un petit modèle d'applet, appelé Example1Applet, qui illustre le contour général d'une applet d'animation. Example1Applet montre comment créer un thread et appeler la repaint()méthode à intervalles fixes. Le nombre d'images par seconde est spécifié en passant un paramètre d'applet. Voici un exemple de ce que vous mettriez dans votre document HTML:


  

Voici Example1Applet.

Remarque:

Cette applet ne dessine encore rien à l'écran. Le dessin sur l'écran est expliqué plus loin. Notez également que l'applet détruit son fil d'animation chaque fois que l'utilisateur quitte la page (ce qui entraîne l' stop()appel de la méthode de l'applet ). Cela garantit que l'applet ne perdra pas de temps CPU tant que sa page n'est pas visible.

Garder une fréquence d'images constante

Dans l'exemple ci-dessus, l'applet se met simplement en veille pendant un laps de temps fixe entre les images. Cela présente l'inconvénient d'attendre parfois trop longtemps. Pour obtenir 10 images par seconde, vous ne devez pas attendre 100 millisecondes entre les images, car vous perdez du temps à exécuter le thread.

L'applet suivant, Example2Applet, montre comment conserver un meilleur temps. Il calcule simplement le délai correct entre les images en gardant une trace de l'heure de début. Il calcule le délai requis estimé entre les trames en fonction de l'heure actuelle.

Voici Example2Applet.

Peindre chaque cadre

Il ne reste plus qu'à peindre chaque cadre. Dans les exemples précédents, nous appelons repaint()chaque frame, ce qui provoque l' appel de la paint()méthode de l'applet . Le Example3Applet a une paint()méthode qui dessine le numéro de l'image actuelle à l'écran.

Voici Example3Applet en action, suivi d'une liste de codes.

Remarque:

Si vous spécifiez une fréquence d'images très élevée (disons 100 images par seconde), la run()méthode appellera repaint()100 fois par seconde. Cependant, cela n'entraînera pas toujours 100 appels paint()par seconde, car lorsque vous émettez une demande de repeinture trop rapidement, ils seront réduits en une seule mise à jour d'écran. C'est pourquoi nous gardons une trace du numéro de trame actuel dans la run()méthode plutôt que dans la paint()méthode.

Générer des graphiques

Maintenant, animons quelque chose qui est un peu plus difficile à dessiner. L'applet Example4 dessine une combinaison d'ondes sinusoïdales. Pour chaque coordonnée x, il trace une courte ligne verticale. Toutes ces lignes forment ensemble un graphique simple qui change pour chaque image. Malheureusement, vous constaterez que cette approche provoque beaucoup de clignotements. Nous expliquerons la cause du clignotement et quelques remèdes dans la section suivante.

Voici Example4Applet en action, suivi d'une liste de codes.

Éviter les clignotements excessifs

Le clignotement que vous voyez dans Example4Applet a deux causes: la peinture de chaque image prend trop de temps (en raison de la quantité de calcul requise pendant la peinture) et tout l'arrière-plan est effacé avant d' paint()être appelé. Pendant le calcul de l'image suivante, l'utilisateur voit l'arrière-plan de l'animation.

Ce court laps de temps entre le dégagement du fond et la peinture de l'onde sinusoïdale est vu comme un éclair. Sur certaines plates-formes comme le PC, le clignotement est plus évident que sur X Windows. La raison en est que les graphiques X Windows sont mis en mémoire tampon, ce qui rend le flash un peu plus court.

Vous pouvez réduire considérablement le clignotement en utilisant deux astuces simples: implémenter la update()méthode et utiliser le double tampon (parfois appelé utilisation d'un backbuffer ).

Remplacer la méthode update ()

Lorsque l'AWT reçoit une demande de repeinture pour une applet, il appelle la update()méthode de l'applet . Par défaut, la update()méthode efface l'arrière-plan de l'applet, puis appelle la paint()méthode. En remplaçant la update()méthode pour inclure le code de dessin qui était auparavant dans la paint()méthode, nous évitons de faire effacer toute la zone de l'applet à chaque repeinture.

Maintenant que l'arrière-plan n'est plus effacé automatiquement, nous devons le faire nous-mêmes dans la update()méthode. Nous pouvons maintenant effacer chaque ligne verticale du graphique individuellement avant de dessiner la nouvelle ligne, éliminant ainsi complètement le clignotement. Cet effet est illustré dans Example5Applet.

Voici Example5Applet en action, suivi d'une liste de codes.

Remarque:

Chaque fois que vous remplacez la update()méthode, vous devez toujours l'implémenter paint(). En effet, la paint()méthode est appelée directement par le système de dessin AWT chaque fois qu'un «dommage» survient dans la zone de dessin de l'applet - par exemple, lorsqu'une fenêtre masquant une partie de la zone de dessin de l'applet est supprimée de l'écran. Votre paint()implémentation peut simplement appeler update().

Double tampon

Une autre façon de réduire le clignotement entre les images consiste à utiliser un double tampon. Cette technique est utilisée dans de nombreuses applets d'animation.

Le principe général est que vous créez une image hors écran, vous dessinez un cadre dans l'image, puis vous giflez l'image entière sur l'écran en un seul appel drawImage(). L'avantage est que la plupart des dessins sont réalisés hors écran. La peinture finale de l'image hors écran sur l'écran est généralement beaucoup plus efficace que la peinture du cadre directement sur l'écran.

L'applet d'onde sinusoïdale avec double tamponnage est illustrée dans Example6Applet. Vous verrez que l'animation est assez fluide et que vous n'avez pas besoin d'astuces spéciales pour dessiner le cadre. Le seul inconvénient est que vous devez allouer une image hors écran aussi grande que la zone de dessin. Si la zone de dessin est très grande, cela peut demander beaucoup de mémoire.

Voici Example6Applet en action, suivi d'une liste de codes.

Remarque:

Lorsque vous utilisez le double tampon, vous devez remplacer la update()méthode, car vous ne voulez pas que l'arrière-plan de l'applet soit effacé avant de peindre le cadre. (Vous effacez vous-même l'arrière-plan en dessinant sur l'image hors écran.)

Utiliser des images

Nous allons maintenant réécrire la paintFrame()méthode avec une méthode qui anime certaines images. Cela ajoute quelques complications mineures au problème. Les images sont plutôt volumineuses et elles sont chargées de manière incrémentielle. Le dessin complet des images peut prendre un certain temps, en particulier lorsque vous les chargez via une connexion lente. C'est la raison pour laquelle la drawImage()méthode prend un quatrième argument, un objet ImageObserver. L'observateur d'image est un objet qui est notifié lorsque davantage de données d'image sont arrivées. Pour obtenir les images, nous utilisons la getImage()méthode.

Déplacement d'une image sur l'écran

Cette première applet d'animation d'image, Example7Applet, utilise les deux images suivantes:

world.gif: voiture.gif:

L'image du monde est utilisée comme arrière-plan et l'image de la voiture est dessinée deux fois dessus, créant une animation de deux voitures qui courent à travers le monde.

Voici Example7Applet en action, suivi d'une liste de codes.

Affichage d'une séquence d'images

Example8Applet montre comment créer une animation en utilisant des images séparées pour chaque image. Voici les 10 cadres qui sont utilisés:

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

T6.gif:

T7.gif:

T8.gif:

T9.gif:

T10.gif:

Nous utilisons toujours un double tampon pour éliminer le clignotement. La raison en est que chaque image que nous rendons est partiellement transparente, et nous devons donc effacer chaque image avant de dessiner la suivante. Cela provoquerait un clignotement sans double tampon.

Voici Example8Applet en action, suivi d'une liste de codes.

Remarque:

Lors de l'affichage de séquences d'images, vous devez veiller à aligner correctement les images. Le moyen le plus simple est de s'assurer que les images ont toutes la même taille et peuvent être dessinées à la même position. Si ce n'est pas le cas, votre applet devra dessiner chaque image avec un décalage différent.

Utilisation de MediaTracker pour éviter l'affichage incrémentiel

Lorsqu'un programme Java charge une image, il peut afficher l'image avant que l'image ne soit complètement chargée. L'utilisateur voit l'image rendue d'abord de manière incomplète, puis de plus en plus complètement au fur et à mesure que l'image est chargée. Cet affichage incrémental donne à l'utilisateur des commentaires (améliorant les performances perçues) et permet au programme d'effectuer facilement d'autres tâches pendant le chargement de l'image.

En ce qui concerne l'animation, l'affichage d'image incrémentiel peut être utile pour les images d'arrière-plan, mais il peut être très gênant lorsqu'il est utilisé pour les images animées. Il est donc parfois souhaitable d'attendre que toute l'animation soit chargée avant de l'afficher.

Vous pouvez utiliser la MediaTrackerclasse de Jim Graham pour suivre le téléchargement des images, retardant l'affichage de l'animation jusqu'à ce que l'ensemble des images soit entièrement téléchargé. Example9Applet montre comment utiliser la MediaTrackerclasse pour télécharger des images pour l'animation de Duke.

Voici Example9Applet en action, suivi d'une liste de codes.

Ajout de son

Il est facile d'ajouter du son à une animation. Vous pouvez utiliser la getAudioClip()méthode pour obtenir un objet AudioClip. Plus tard, vous pouvez lire le clip en boucle continue ou en un seul son. Example10Applet montre comment jouer un fond sonore continu ainsi qu'un son répétitif pendant l'animation.

Voici Example10Applet en action, suivi d'une liste de codes.

Remarque:

Lorsque vous jouez un son continu, vous devez vous rappeler de l'arrêter lorsque l'utilisateur quitte la page (c'est-à-dire, faites-le dans la stop()méthode de votre applet ).

Une autre note:

L'audio continu peut être très ennuyeux. C'est une bonne idée de fournir à l'utilisateur un moyen de désactiver l'audio sans quitter la page. Vous pouvez fournir un bouton ou simplement désactiver l'audio lorsque l'utilisateur clique dans l'applet.

Conseils pour charger les images plus rapidement

Une animation qui utilise de nombreuses images prendra beaucoup de temps à télécharger. Cela est principalement dû au fait qu'une nouvelle connexion HTTP est établie pour chaque fichier image, et l'établissement d'une connexion peut prendre plusieurs secondes même lorsqu'il y a beaucoup de bande passante.

Dans cette section, nous vous parlerons de deux formats d'image que votre applet peut utiliser pour accélérer le téléchargement d'images.

Utilisation d'une bande d'image

Vous pouvez améliorer les performances de téléchargement en utilisant une seule image contenant plusieurs images d'animation. Vous pouvez rendre une seule image hors de l'image à l'aide de l' clipRect()opérateur. Voici un exemple de bande d'image utilisée dans l'applet UnderConstruction.

L'applet crée un effet de perçage en n'effaçant pas les images précédentes. L'arrière-plan n'est effacé que de temps en temps.

Voici UnderConstruction en action, avec un lien vers son code source.

Compression inter-image avec Flic

Si vous voulez vraiment améliorer les performances de téléchargement d'une animation composée de plusieurs images, vous devez utiliser une forme de compression inter-images.

Outils d'animation

En ce moment (janvier 1996), peu d'outils sont disponibles pour vous aider à créer des animations Java. Le meilleur outil que j'ai pu trouver est The Easy Animator (TEA) de DimensionX (anciennement JAM). Il vous permet de créer des animations de manière interactive. Nous aimerions encourager les développeurs à écrire plus d'outils pour la création d'animations en Java.

Si vous avez quelques images prêtes à l'emploi à afficher, vous pouvez utiliser l'applet Animator. Animator a de nombreux paramètres qui vous permettent de spécifier des sons continus, des sons spécifiques à l'image, une synchronisation et des positions d'image individuelles, une image de démarrage, l'ordre des images, etc.

Vous devriez également consulter la page Gamelan Animation pour trouver de nombreuses applets utilisant l'animation.

Conclusion

J'espère que cet article aidera les développeurs d'applets à écrire plus et de meilleures applets d'animation. J'espère également que de meilleurs outils seront bientôt disponibles.

Arthur van Hoff était, jusqu'à récemment, un ingénieur senior chez Sun Microsystems et a été impliqué dans le développement du langage Java depuis 1993. Il est l'auteur du premier compilateur Java entièrement écrit en Java. Il a récemment quitté Sun pour créer une nouvelle société avec Sami Shaio, Kim Polese et Jonathan Payne. La nouvelle société se concentrera sur la création d'applications Java. Kathy Walrath est rédactrice technique chez Sun Microsystems. Elle fait partie de l'équipe Java depuis 1993. Actuellement, elle travaille avec Mary Campione sur The Java Tutorial: Object-Oriented Programming for the Internet, un didacticiel amélioré par applet pour apprendre le langage Java, la programmation d'applet et la programmation Java GUI . En plus d'être disponible en ligne, The Java Tutorial sera également publié cet été dans le cadre de la série Addison-Wesley Java.

Cette histoire, "Animation dans les applets Java" a été initialement publiée par JavaWorld.