Subprocesos (threads) |
Pasos para crear una animación
Cargando y mostrando las imágenes
Aunque 12 imágenes por segundo son suficientes para producir la ilusión de movimiento, en el cine se emplean del orden de 24 y en la televisión del orden de 30. El número de imágenes por segundo que se pueden producir y mostrar con un ordenador dependen de su potencia de cálculo.
Resumimos los pasos necesarios para crear una animación.
public class AnimaApplet4 extends Applet implements Runnable { //... }
if (anima == null) { anima = new Thread(this); anima.start(); }
if (anima != null) { anima.stop(); anima = null; }
public void run() { long t=System.currentTimeMillis(); while (true) { //tarea a realizar ....mover una pelota, etc. try{ t+=retardo; Thread.sleep(Math.max(0, t-System.currentTimeMillis())); }catch(InterruptedException ex){ break; } } }
- Crea un contexto gráfico en memoria gBuffer, y una imagen imag de las mismas dimensiones que el componente.
- Borra dicho contexto con el color de fondo del componente.
- Dibuja sobre dicho contexto gráfico en memoria gBuffer
- Transfiere la imagen en memoria imag al contexto gráfico del componente g, mediante drawImag.
public void update(Graphics g) { if (gBuffer == null) { imag = createImage(ancho, alto); gBuffer = imag.getGraphics(); } gBuffer.setColor(getBackground()); gBuffer.fillRect(0, 0, ancho, alto); gBuffer.setColor(Color.black); //aquí se dibuja sobre el contexto gráfico en memoria gBuffer //.... g.drawImage(imag, 0, 0, null); }
anima4:
AnimaApplet4.java, 0.gif,
1.gif, 2.gif, 3.gif, 4.gif, 5.gif, 6.gif, 7.gif, 8.gif, 9.gif
En este ejemplo, vamos a ver lo específico de cada tipo de animación. En este caso es un contador. Los números del cero al nueve, se representan por pequeñas imágenes que se han guardado en archivos .GIF.
El primer paso, será cargar las imágenes mediante getImage y guardarlas en un array de objetos de la clase Image. Las imágenes las hemos situado en un subdirectorio denominado contadorGif por debajo de la ubicación del archivo HTM que guarda la página que contiene el applet. El nombre de los archivos como podemos apreciar en la porción de código es 1.gif, 2.gif ... 9.gif
for (int i = 0; i < 10; i++) { numeros[i] = getImage(getDocumentBase(), "contadorGif/" + i + ".gif"); }
En el bucle while de la función miembro run se incrementa el contador de imágenes. Cuando el contador indice sobrepasa el número nueve comienza con el cero, y así se repite el ciclo.
public void run() { while (true) { if (++indice> numeros.length){ indice=0; } repaint(); //... } }
Cuando hay subprocesos corriendo, no podemos sustituir las sentencias
if (++indice> numeros.length){ indice = 0; }
Por las sentencias
indice++; if (indice>= numeros.length){ indice = 0; }
Sino por las sentencias
synchronized (this) { indice++; if (indice>= numeros.length) { indice=0; } }
Finalmente, repaint llama a update para dibujar la imagen guardada en el elemento indice del array numeros en el contexto gráfico en memoria gBuffer empleando la técnica del double-buffer.
public void update(Graphics g) { //... gBuffer.drawImage(numeros[indice], 0, 0, this); //... }
Como podemos apreciar al visitar una página web, una imagen estática grande se va mostrando a medida que se va cargando. En una animación con un conjunto de imágenes esto no es posible, tenemos que disponer de todas las imágenes antes de correr (run) la animación.
El retraso en la transmisión es el tiempo que transcurre hasta que un determinado objeto ha sido transferido a través de la red Internet. La clase MediaTracker nos ayuda a resolver los problemas relacionados con el retraso en la transmisión de imágenes, sonido u otros elementos multimedia.
Para disponer de todas las imágenes antes de empezar la animación, en init creamos un objeto tracker de la clase MediaTracker.
tracker = new MediaTracker(this);
A medida que vamos cargando las imágenes mediante getImage, las vamos añadiendo al objeto tracker, mediante addImage. El segundo argumento, 0, es el identificador de un grupo de imágenes.
for (int i = 0; i < 10; i++) { numeros[i] = getImage(getDocumentBase(), "contadorGif/" + i + ".gif"); tracker.addImage(numeros[i], 0); }
Nos aseguramos de que todas las imágenes, cuyo identificador es 0, se ha cargado mediante las siguientes líneas de código
try { tracker.waitForID(0); }catch (InterruptedException ex) { return; }
El código fuente completo, es el siguiente
public class AnimaApplet4 extends Applet implements Runnable { Image[] numeros = new Image[10]; Thread anima; MediaTracker tracker; int retardo = 800; int indice = 0; //doble buffer Image imag; Graphics gBuffer; public void init() { // carga las imágenes tracker = new MediaTracker(this); for (int i = 0; i < 10; i++) { numeros[i] = getImage(getDocumentBase(), "contadorGif/" + i + ".gif"); tracker.addImage(numeros[i], 0); } } public void start() { if (anima == null) { anima = new Thread(this); anima.start(); } } public void stop() { if (anima != null) { anima.stop(); anima = null; } } public void run() { //han de estar completamente cargadas las imágnes antes de empezar a correr la animación try { tracker.waitForID(0); }catch (InterruptedException ex) { return; } // corre la animación long t = System.currentTimeMillis(); while (true) { synchronized (this) { indice++; if (indice>=numeros.length) { indice=0; } } repaint(); try { t+=retardo; Thread.sleep(Math.max(0, t - System.currentTimeMillis())); }catch(InterruptedException e) { break; } } } public void update(Graphics g) { int ancho=getSize().width; int alto=getSize().height; if (gBuffer == null) { imag = createImage(ancho, alto); gBuffer = imag.getGraphics(); } gBuffer.setColor(getBackground()); gBuffer.fillRect(0, 0, ancho, alto); gBuffer.setColor(Color.black); // muestra las imágenes gBuffer.drawImage(numeros[indice], 0, 0, this); // Transfiere la imagen off a al contexto gráfico del applet g.drawImage(imag, 0, 0, null); } public void paint(Graphics g) { Font font = new Font("TimesRoman", Font.BOLD, 16); g.setFont(font); FontMetrics fm = g.getFontMetrics(font); String texto; if ((tracker.statusID(0, true) == MediaTracker.LOADING)) { //se están cargando las imágenes texto="Cargando las imágenes ...."; g.drawString(texto, (getSize().width - fm.stringWidth(texto)) / 2, ((getSize().height - fm.getHeight()) / 2) + fm.getAscent()); } } } |