Animazione in applet Java

Questo articolo descrive come implementare l'animazione utilizzando l'API dell'applet Java. Descrive le tecniche di uso comune e fornisce un semplice esempio per illustrare ciascuna tecnica.

Tecniche di animazione di base

Molte forme di animazione sono possibili in Java. Ciò che tutti hanno in comune è che creano una sorta di movimento sullo schermo disegnando fotogrammi successivi a una velocità relativamente alta (di solito circa 10-20 volte al secondo).

Inizieremo creando un semplice applet modello per fare animazioni e lo elaboreremo lentamente fino ad arrivare a un applet abbastanza completo.

Usando un filo

Per aggiornare lo schermo più volte al secondo, è necessario creare un nuovo thread Java che contenga un ciclo di animazione. Il ciclo di animazione è responsabile di tenere traccia del fotogramma corrente e di richiedere aggiornamenti periodici dello schermo. Per implementare un thread, è necessario creare una sottoclasse Threado aderire Runnableall'interfaccia.

Un errore comune è inserire il ciclo di animazione nel paint()metodo di un'applet. In questo modo si avranno strani effetti collaterali perché regge il thread AWT principale, che è responsabile di tutti i disegni e la gestione degli eventi.

Come esempio ho scritto una piccola applet modello, chiamata Example1Applet, che illustra lo schema generale di un'applet di animazione. Example1Applet mostra come creare un thread e chiamare il repaint()metodo a intervalli fissi. Il numero di frame al secondo viene specificato passando un parametro applet. Ecco un esempio di cosa inseriresti nel tuo documento HTML:


  

Ecco Example1Applet.

Nota:

Questa applet in realtà non disegna ancora nulla sullo schermo. Il disegno sullo schermo viene spiegato più avanti. Nota anche che l'applet distrugge il suo thread di animazione ogni volta che l'utente lascia la pagina (il che si traduce nella stop()chiamata del metodo dell'applet ). Ciò garantisce che l'applet non sprechi tempo della CPU mentre la sua pagina non è visibile.

Mantenere un frame rate costante

Nell'esempio sopra, l'applet dorme semplicemente per un periodo di tempo fisso tra i frame. Questo ha lo svantaggio che a volte aspetti troppo a lungo. Per ottenere 10 fotogrammi al secondo non dovresti aspettare 100 millisecondi tra i fotogrammi, perché perdi tempo solo eseguendo il thread.

La seguente applet, Example2Applet, mostra come mantenere il tempo migliore. Calcola semplicemente il ritardo corretto tra i frame tenendo traccia dell'ora di inizio. Calcola il ritardo richiesto stimato tra i frame in base all'ora corrente.

Ecco Example2Applet.

Dipingere ogni fotogramma

Ciò che resta è dipingere ogni fotogramma. Negli esempi precedenti, chiamiamo repaint()per ogni frame, che causa la paint()chiamata del metodo dell'applet . Example3Applet ha un paint()metodo che disegna il numero del frame corrente sullo schermo.

Ecco Example3Applet in azione, seguito da un listato di codice.

Nota:

Se specifichi che la frequenza dei fotogrammi è molto alta (diciamo 100 fotogrammi al secondo), il run()metodo chiamerà repaint()100 volte al secondo. Tuttavia, ciò non sempre si tradurrà in 100 chiamate a paint()al secondo perché quando si emette una richiesta di ridisegno troppo rapidamente, verranno compresse in un unico aggiornamento della schermata. Questo è il motivo per cui teniamo traccia del numero di frame corrente nel run()metodo piuttosto che nel paint()metodo.

Generazione di grafici

Ora animiamo qualcosa che è un po 'più difficile da disegnare. Example4Applet disegna una combinazione di onde sinusoidali. Per ogni coordinata x, traccia una breve linea verticale. Tutte queste linee insieme formano un semplice grafico che cambia per ogni fotogramma. Sfortunatamente, scoprirai che questo approccio causa molti lampeggi. Spiegheremo la causa del lampeggiamento e alcuni rimedi nella sezione successiva.

Ecco Example4Applet in azione, seguito da un listato di codice.

Evitare lampi eccessivi

Il lampeggiamento che vedi in Example4Applet ha due cause: dipingere ogni fotogramma richiede troppo tempo (a causa della quantità di calcolo richiesta durante il ridipingere) e l'intero sfondo viene cancellato prima di paint()essere chiamato. Mentre è in corso il calcolo del fotogramma successivo, l'utente vede lo sfondo dell'animazione.

Questo breve tempo tra lo schiarimento dello sfondo e la pittura dell'onda sinusoidale è visto come un lampo. Su alcune piattaforme come il PC, il lampeggiamento è più evidente rispetto a quello su X Windows. Il motivo è che la grafica di X Windows è bufferizzata, il che rende il flash un po 'più breve.

È possibile ridurre notevolmente il flashing utilizzando due semplici trucchi: implementare il update()metodo e utilizzare il doppio buffering (a volte noto come utilizzo di un backbuffer ).

Sovrascrivere il metodo update ()

Quando l'AWT riceve una richiesta di ridisegno per un'applet, chiama il update()metodo dell'applet . Per impostazione predefinita, il update()metodo cancella lo sfondo dell'applet e quindi chiama il paint()metodo. Sovrascrivendo il update()metodo per includere il codice di disegno che era nel paint()metodo, evitiamo che l'intera area dell'applet venga cancellata ad ogni ridisegno.

Ora che lo sfondo non viene più cancellato automaticamente, dobbiamo farlo da soli nel update()metodo. Ora possiamo cancellare singolarmente ogni linea verticale del grafico prima di tracciare la nuova linea, eliminando completamente il lampeggiamento. Questo effetto è mostrato in Example5Applet.

Ecco Example5Applet in azione, seguito da un listato di codice.

Nota:

Ogni volta che si sovrascrive il update()metodo, è comunque necessario implementarlo paint(). Questo perché il paint()metodo viene chiamato direttamente dal sistema di disegno AWT ogni volta che si verifica un "danno" all'area di disegno dell'applet, ad esempio quando una finestra che oscura parte dell'area di disegno dell'applet viene rimossa dallo schermo. La tua paint()implementazione può semplicemente chiamare update().

Doppio buffering

Un altro modo per ridurre il lampeggiamento tra i fotogrammi è utilizzare il doppio buffering. Questa tecnica viene utilizzata in molte applet di animazione.

Il principio generale è che si crea un'immagine fuori schermo, si disegna una cornice nell'immagine e quindi si schiaffeggia l'intera immagine sullo schermo con una chiamata a drawImage(). Il vantaggio è che la maggior parte del disegno viene eseguito fuori schermo. La pittura finale dell'immagine fuori schermo sullo schermo è solitamente molto più efficiente rispetto alla pittura della cornice direttamente sullo schermo.

L'applet dell'onda sinusoidale con doppio buffering è mostrata in Example6Applet. Vedrai che l'animazione è abbastanza fluida e non hai bisogno di trucchi speciali per disegnare la cornice. L'unico svantaggio è che devi allocare un'immagine fuori schermo grande quanto l'area di disegno. Se l'area di disegno è molto grande, potrebbe essere necessaria molta memoria.

Ecco Example6Applet in azione, seguito da un listato di codice.

Nota:

Quando usi il doppio buffering, devi sovrascrivere il update()metodo, poiché non vuoi che lo sfondo dell'applet venga cancellato prima di dipingere la cornice. (Puoi cancellare lo sfondo da solo disegnando sull'immagine fuori schermo.)

Usare le immagini

Ora riscriveremo il paintFrame()metodo con un metodo che anima alcune immagini. Ciò aggiunge alcune complicazioni minori al problema. Le immagini sono piuttosto grandi e vengono caricate in modo incrementale. Il disegno completo delle immagini può richiedere molto tempo, soprattutto quando le carichi con una connessione lenta. Questo è il motivo per cui il drawImage()metodo accetta un quarto argomento, un oggetto ImageObserver. L'osservatore dell'immagine è un oggetto che viene avvisato quando sono arrivati ​​più dati dell'immagine. Per ottenere le immagini usiamo il getImage()metodo.

Spostare un'immagine sullo schermo

Questa prima applet di animazione di immagini, Example7Applet, utilizza le due immagini seguenti:

world.gif: car.gif:

L'immagine del mondo viene utilizzata come sfondo e l'immagine dell'auto viene disegnata sopra di essa due volte, creando un'animazione di due auto che corrono in tutto il mondo.

Ecco Example7Applet in azione, seguito da un listato di codice.

Visualizzazione di una sequenza di immagini

Esempio8Applet mostra come creare un'animazione utilizzando immagini separate per ogni fotogramma. Ecco i 10 frame che vengono utilizzati:

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

T6.gif:

T7.gif:

T8.gif:

T9.gif:

T10.gif:

Stiamo ancora utilizzando il doppio buffering per eliminare il flashing. Il motivo è che ogni immagine che stiamo renderizzando è parzialmente trasparente, quindi dobbiamo cancellare ogni fotogramma prima di disegnare il successivo. Ciò causerebbe il lampeggiamento senza doppio buffering.

Ecco Example8Applet in azione, seguito da un listato di codice.

Nota:

Quando si visualizzano sequenze di immagini, è necessario fare attenzione ad allineare correttamente le immagini. Il modo più semplice è assicurarsi che le immagini abbiano tutte le stesse dimensioni e possano essere disegnate nella stessa posizione. In caso contrario, l'applet dovrà disegnare ogni fotogramma con un offset diverso.

Utilizzo di MediaTracker per evitare la visualizzazione incrementale

Quando un programma Java carica un'immagine, può visualizzare l'immagine prima che l'immagine sia completamente caricata. L'utente vede l'immagine renderizzata prima in modo incompleto e poi in modo incrementale sempre più completo man mano che l'immagine viene caricata. Questo display incrementale fornisce un feedback all'utente (migliorando le prestazioni percepite) e consente al programma di eseguire facilmente altre attività durante il caricamento dell'immagine.

Per quanto riguarda l'animazione, la visualizzazione incrementale delle immagini può essere utile per le immagini di sfondo, ma può essere molto fonte di distrazione se utilizzata per le immagini animate. Pertanto a volte è consigliabile attendere il caricamento dell'intera animazione prima di visualizzarla.

È possibile utilizzare la MediaTrackerclasse di Jim Graham per tenere traccia del download di immagini, ritardando la visualizzazione dell'animazione fino al completo download dell'intero set di immagini. Example9Applet mostra come usare la MediaTrackerclasse per scaricare immagini per l'animazione di Duke che sventola.

Ecco Example9Applet in azione, seguito da un listato di codice.

Aggiunta di suoni

È facile aggiungere suoni a un'animazione. È possibile utilizzare il getAudioClip()metodo per ottenere un oggetto AudioClip. Successivamente, puoi riprodurre la clip come un loop continuo o come un singolo suono. Esempio10Applet mostra come riprodurre un suono di sottofondo continuo così come un suono ripetitivo durante l'animazione.

Ecco Example10Applet in azione, seguito da un listato di codice.

Nota:

Quando si riproduce un suono continuo, è necessario ricordarsi di interromperlo quando l'utente lascia la pagina (ovvero, farlo con il stop()metodo dell'applet ).

Un'altra nota:

L'audio continuo può essere molto fastidioso. È una buona idea fornire all'utente un modo per disattivare l'audio senza lasciare la pagina. Puoi fornire un pulsante o semplicemente disattivare l'audio quando l'utente fa clic nell'applet.

Suggerimenti per caricare le immagini più velocemente

Il download di un'animazione che utilizza molte immagini richiederà molto tempo. Ciò è dovuto principalmente al fatto che viene creata una nuova connessione HTTP per ogni file di immagine e la creazione di una connessione può richiedere diversi secondi anche quando c'è molta larghezza di banda.

In questa sezione, ti parleremo di due formati di immagine che l'applet può utilizzare per velocizzare il download delle immagini.

Utilizzando una striscia di immagine

È possibile migliorare le prestazioni di download utilizzando una singola immagine contenente diversi fotogrammi di animazione. È possibile eseguire il rendering di un singolo fotogramma fuori dall'immagine utilizzando l' clipRect()operatore. Di seguito è riportato un esempio di una striscia di immagine utilizzata nell'applet UnderConstruction.

L'applet crea un effetto di perforazione non cancellando i frame precedenti. Lo sfondo viene cancellato solo ogni tanto.

Ecco UnderConstruction in azione, con un collegamento al suo codice sorgente.

Compressione tra fotogrammi utilizzando Flic

Se vuoi davvero migliorare le prestazioni di download di un'animazione composta da più fotogrammi, devi utilizzare una qualche forma di compressione inter-frame.

Strumenti di animazione

In questo momento (gennaio 1996), sono disponibili pochi strumenti per aiutarti a creare animazioni basate su Java. Lo strumento migliore che ho trovato è The Easy Animator (TEA) di DimensionX (precedentemente noto come JAM). Ti consente di creare animazioni in modo interattivo. Vorremmo incoraggiare gli sviluppatori a scrivere più strumenti per la creazione di animazioni in Java.

Se hai alcune immagini già pronte da visualizzare, puoi utilizzare l'applet Animator. Animator dispone di molti parametri che consentono di specificare suoni continui, suoni specifici per fotogrammi, tempi e posizioni dei singoli fotogrammi, un'immagine di avvio, l'ordine dei fotogrammi e così via.

Dovresti anche controllare la pagina Gamelan Animation per trovare molte applet che utilizzano l'animazione.

Conclusione

Spero che questo articolo aiuti gli sviluppatori di applet a scrivere più applet di animazione migliori. Spero anche che presto saranno disponibili strumenti migliori.

Arthur van Hoff era, fino a poco tempo fa, un ingegnere senior presso Sun Microsystems ed è stato coinvolto nello sviluppo del linguaggio Java dal 1993. È l'autore del primo compilatore Java scritto interamente in Java. Di recente ha lasciato Sun per formare una nuova società insieme a Sami Shaio, Kim Polese e Jonathan Payne. La nuova società si concentrerà sulla creazione di applicazioni Java. Kathy Walrath è una redazione tecnica presso Sun Microsystems. Fa parte del team Java dal 1993. Attualmente sta lavorando con Mary Campione su The Java Tutorial: Object-Oriented Programming for the Internet, un tutorial ottimizzato per applet per l'apprendimento del linguaggio Java, programmazione applet e programmazione GUI Java . Oltre ad essere disponibile online, quest'estate sarà pubblicato anche The Java Tutorial come parte della Addison-Wesley Java Series.

Questa storia, "Animation in Java applet" è stata originariamente pubblicata da JavaWorld.