9. Animacja
9.1. Prosta animacja
Ruchomy obraz powstaje przez wyświetlanie kolejnych obrazów statycznych (klatek filmu).
Jeżeli klatki są proste to można je tworzyć na bieżąco w metodzie
paint ( ) za pomocą narzędzi rysowania figur. Pokazuje to przykład apletu Kropka
Przykład
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
public class Kropka extends java.applet.Applet {
Font f; // czcionka
int x, y; // współrzędne kropki
int xp, xk; // zakres ruchu
int dx; // przyrost x
int razy; // liczba kroków
static final int D = 10; // średnica kropki
static final int DX = 1; // absolutny przyrost x
static final int RAZY = 2; // liczba przejazdów
static final int T = (int)1.5E6; // opóźnienie
public void init ( ) {
xp = getSize ( ).width / 10;
xk = getSize ( ).width - xp;
dx = -DX;
x = xp + dx;
y = getSize ( ).height / 2;
razy = RAZY * ( xk - xp ) / DX;
setBackground ( Color.blue );
f = new Font( "Courier", Font.BOLD, 24 );
}
public void pause ( int razy ) {
for ( int i = 0; i < razy; i++ );
}
public void paint ( Graphics g ) {
int i = 1;
while ( i <= razy ) {
g.setColor ( Color.red );
g.fillOval ( x, y, D, D );
pause ( T );
g.setColor ( getBackground ( ) );
g.fillOval ( x, y, D, D );
if ( x < xp || x > xk ) dx = -dx;
x += dx;
i++;
}
g.setColor ( Color.yellow );
g.setFont( f );
g.drawString( "Koniec i kropka", xp, y );
}
}
9.2. Korzystanie z wątków
Klatki bardziej złożone należy przygotować zawczasu i skopiować do tablicy typu Image. Wyświetlanie elementów tej tablicy powinno przebiegać w osobnym wątku.
Przed wyświetleniem kolejnej klatki animacyjnej należy wymazać z ekranu klatkę poprzednią Robi to automatycznie metoda repaint ( ). Wywołuje ona bowiem metodę
public void update(Graphics g) {
g.clearRect(0, 0, width, height);
paint(g);
}
Wywołanie metody repaint ( ) bez parametrów powoduje natychmiastowe przerysowywanie całego apletu. Zwykle stosowane są wersje z takimi parametrami:
repaint ( long tm ) |
odświeża cały aplet co tm milisekund |
repaint ( int x, int y, int width, int height) |
odświeża natychmiast prostokąt o współrzędnych x, y i wymiarach width, height |
repaint ( long tm, int x, int y, int width, int height ) |
odświeża co tm milisekund prostokąt o współrzędnych x, y i wymiarach width, height |
Przykład
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Color;
public class Kino extends java.applet.Applet
implements Runnable {
Image klatki [ ] = new Image [5];
Image klatka; // aktualnie wyświetlana
Thread watek;
int nr; // indeks klatki
int d; // przyrost numeru klatki
static final int X = 20; // współrzędna początkowa
static final int Y = 50; // poziom ruchu
static final long T = 200; // opóźnienie
public void init ( ) {
nr = 0;
d = -1;
setBackground ( Color.blue );
String nazwy [ ] = { "Rysunki/A0.gif",
"Rysunki/A1.gif",
"Rysunki/A2.gif",
"Rysunki/A3.gif",
"Rysunki/A4.gif" };
for (int i = 0; i < klatki.length; i++ ) {
klatki [ i ] = getImage ( getCodeBase ( ),
nazwy [ i ] );
}
watek = new Thread ( this );
}
public void start ( ) {
if ( watek.isAlive ( ) )
watek.resume ( );
else
watek.start ( );
}
public void stop ( ) {
watek.suspend ( );
}
public void destroy ( ) {
watek.stop ( );
}
public void pause ( long T ) {
try { Thread.sleep ( T ); }
catch ( InterruptedException e ) { }
}
public void run ( ) {
while ( true ) {
klatka = klatki [ nr ];
repaint ( );
pause ( T );
if ( nr <= 0 || nr >= 4 ) d = -d;
nr += d;
}
}
public void paint ( Graphics g ) {
if ( klatka != null )
g.drawImage ( klatka, X, Y, this );
}
}
9.3. Zapobieganie migotaniu obrazu
Przyczyną migotania jest zbyt wolne odświeżanie klatek na ekranie. Można z tym walczyć na różne sposoby, m.in.:
przerysowując tylko tę cześć apletu, która powinna ulec zmianie;
przygotowując nową klatkę w tle i kopiując ją na ekran w gotowej postaci.
Ten drugi sposób zwany jest buforowaniem obrazu. Bufor graficzny tworzymy jako obiekt klasy Image za pomocą metody
public Image createImage ( int width, int height )
Następnie pobieramy własności tego obiektu (kontekst) za pomocą metody
public Graphics getGraphics ( )
i umieszczamy je w odpowiedniej zmiennej.
Przykład KinoB pokazuje animację z buforowaniem. Zamiast wyświetlania kolejnych klatek na całej powierzchni apletu, jak w poprzednim przykładzie, wyświetlamy tu jeden obrazek przesuwając go wzdłuż ekranu.
Przykład
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Color;
public class KinoB extends java.applet.Applet
implements Runnable {
Thread watek;
Image klatka; // aktualnie wyświetlana klatka
Image bufor; // bufor graficzny
Graphics konBuf; // kontekst bufora
int xp, xk; // zakres ruchu
int x; // aktualna współrzędna klatki
int dx; // przesunięcie klatki
int y; // poziom ruchu
int b, h; // wymiary klatki
int bApl, hApl; // wymiary apletu
static final int DX = 1; // przesunięcie klatki
static final int T = 15; // opóźnienie
public void init ( ) {
// załadowanie obrazka
klatka = getImage ( getCodeBase ( ),
"Rysunki/AA.gif" );
b = -1; h = -1;
// pobranie jego wymiarów
do {
b = klatka.getWidth ( this );
h = klatka.getHeight ( this );
}
while ( b == -1 || h == -1 );
// pobranie wymiarów apleta
bApl = getSize ( ).width;
hApl = getSize ( ).height;
// określenie parametrów ruchu
xp = 0;
xk = bApl - b;
dx = -DX;
x = xp+dx;
y = hApl / 2 - h/2;
// utworzenie bufora graficznego
bufor = createImage ( bApl, hApl);
konBuf = bufor.getGraphics ( );
}
public void start ( ) {
if ( watek == null ){
watek = new Thread ( this );
watek.start ( );
}
}
public void run ( ) {
while ( true ) {
repaint ( );
pause ( T );
if ( x < xp || x > xk ) dx = -dx;
x += dx;
}
}
void pause ( int czas ) {
try { Thread.sleep ( czas ); }
catch ( InterruptedException e ) { }
}
public void update ( Graphics g ) {
paint ( g );
}
public void paint ( Graphics g ) {
// wypełnianie tła
konBuf.setColor ( Color.blue );
konBuf.fillRect ( 0, 0, bApl, hApl );
// rysowanie na niewidocznej powierzchni
konBuf.drawImage ( klatka, x, y, this );
// kopiowanie do apletu
g.drawImage ( bufor, 0, 0, this );
}
}
Adam Borkowski Język programowania „Java” 9−9