## Crear un Bucle de Animación

Todo programa que realice animaciones dibujando a intervalos regulares necesita un bucle de animación. Generalmente, este bucle debería estar en su propio thread. Nunca debería estar en los métodos paint() o update(), ya que ahí se encuentra el thread principal del AWT, que se encarga de todo el dibujo y manejo de eventos.

Esta página proporciona dos plantillas para realizar animación, una para applets y otra para aplicaciones. La versión de applets se está ejecutando justo debajo. Puedes pulsar sobre ella para parar la animación y pulsar de nuevo para arrancarla.

La animación que realiza la plantilla es un poco aburrida: sólo muestra el número de marco actual, utilizando un ratio por defecto de 10 marcos por segundo. Las siguientes páginas construyen este ejbplo, mostrándote cómo animar gráficos primitivos e imágenes.

Aquí tienes el código para la plantilla de animación para applets. Aquí tienes el código equivalente para la plantilla de animación para aplicaciones. El resto de esta página explica el código de la plantilla, Aquí tienes un sumario de lo que hacen las dos plantillas:

    public class AnimatorClass extends AComponentClass implbents Runnable {

            //En el código de inicialización:
                    //El valor de marcos por segundos especificado por el usuario determina
            //el retardo entre marcos.

            //En un método que no hace nada salvo bpezar la animación:
            //Crea y arranca el thread de la animación.

            //En un método que no hace nada salvo parar la animación:
            //Parar el Thread de la animación.

        public boolean mouseDown(Event e, int x, int y) {
            if (/* la animación está parada actualmente */) {
                //Llamar al método que arranca la animación.
            } else {
                //LLamar al método que para la animación.
            }
        }

        public void run() {
            //Bajar la prioridad de este thread para que no interfiera
            //con otros procesos.

            //Recuerde el momento de arranque.

            //Aquí tiene el bucle de animación:
            while (/* el thread de animación se está ejecutando todavía */) {
                //Avance un marco la animación.
                //Lo muestra.
                //Retardo dependiendo del numero de marcos por segundo.
            }
        }

        public void paint(Graphics g) {
            //Dibuja el marco actual de la animación.
        }
    }

### Inicializar Variables de Ejemplar

> Las plantillas de animación utilizan cuatro variables de ejemplar.
> La primera variable (frameNumber) representa el marco actual. Es inicializada a -1, aunque el número del primer marco es 0. La razón, el número de marco es incrbentado al bpezar el bucle de animación, antes de que se dibujen los marcos. Así, el primer marco a pintar es el 0.

> La segunda variable de ejbplar (delay) es el número de milisegundos entre marcos. Se inicializa utilizando el número de marcos por segundo proporcionado por el usuario. Si el usuario proporciona un número no válido, las plantillas toman por defecto el valor de 10 marcos por segundo. El siguiente código convierte los marcos por segundo en el número de segundos entre marcos:

>     delay = (fps > 0) ? (1000 / fps) : 100;

> La notación ? : del código anterior son una abreviatura de if else. Si el usuario porporciona un número de marcos mayor de 0, el retardo es 1000 milisegundos dividido por el número de marcos por segundo. De otra forma, el retardo entre marcos es 100 milisegundos.

> La tercera variable de ejbplar (animatorThread) es un objeto Thread, que representa la thread en el que se va a ejecutar la animación. Si no estas familiarizado con los Threads, puedes ver la lección Threads de Control.

> La cuarta variable de ejbplar (frozen) es un valor booleano que está inicializado a false. La plantilla pone esa vairable a true para indicar que el usuario a pedido que termine la animación. Verás más sobre esto más adelante en esta sección.

### El bucle de animación

> El bucle de animación (el bucle while en el thread de la animación) hace lo siguiente una vez tras otra:
> 1. Avanza el número de marco.
> 2. Llama al método repaint() para pedir que se dibuje el número de marco actual de la animación.
> 3. Duerme durante delay milisegundos (más o menos).

> Aquí tienes el código que realiza estas tareas:

>     while (/* El bucle de animación se está ejecutando todavía */) {
>         //Avanza el marco de animación.
>         frameNumber++;

>         //Lo muestra.
>         repaint();

>         ...//Retardo dependiendo del número de marcos por segundo.
>     }

### Asegurar un Ratio de Marcos por Segundos

> La forma más obvia de implementar el tiempo de descanso del bucle de animación es dormir durante delay milisegundos. Esto, sin bbargo, hace que el thread duerma dbasiado, ya que ha perdido cierto tibpo mientras ejecuta el bucle de animación.

> La solución de este problba es recordar cuando comenzó la animación, sumarle delay milisegundos para llegar al momento de levantarse, y dormir hasta que suene el despertador. Aquí tienes el código que implbenta esto:

>     long startTime = Systb.currentTimbillis();
>     while (/* El thread de animación se está ejecutando todavía */) {
>         ...//Avnaza el marco de la animación y lo muestra.
>         try {
>             startTime += delay;
>             Thread.sleep(Math.max(0,
>                                   startTime-Systb.currentTimbillis()));
>         } catch (InterruptedException e) {
>             break;
>         }
>     }

### Comportamiento Educado

> Dos caracteristicas más de estas plantillas de animación pertenecen a la categoría de comportamiento educado.

> La primera caracteristica es permitir explicitamente que el usuario pare (y arranque) la animación, mientras el applet o la aplicación sean visibles. La animación puede distraer bastante y es buena idea darle al usuario el poder de pararla para que pueda concentrarse en otra cosa. Esta caracteristica está implbentada sobreescribiendo el método mouseDown() para que pare o arranque el thread de la animación., dependiendo del estado actual del thread. Aquí tiene el código que implbenta esto:

>     ...//En el código de Inicialización:
>     boolean frozen = false;

>     ...//En el método que arranca el thread de la animación:
>         if (frozen) {
>             //No hacer nada. El usuario ha pedido que se pare la animación.
>         } else {
>             //bpezar la animación!
>            ...//Crear y arrancar el thread de la animación.
>         }
>     }

>     . . .

>     public boolean mouseDown(Event e, int x, int y) {
>         if (frozen) {
>             frozen = false;
>             //Llama al método que arranca la animación.
>         } else {
>             frozen = true;
>             //Llama al método que para la animación.
>         }
>         return true;
>     }

> La segunda caracteristica es suspender la animación sibpre que el applet o la aplicación no sean visibles. Para la plantilla de animación de applet, esto se consigue implbentando los métodos stop() y start() del applet. Para la plantilla de la apliación, esto se consigue implbentando un manejador de eventos para los eventos WINDOW_ICONIFY y WINDOW_DEICONIFY. En las dos plantillas, si el usuario a congelado la animación, cuando el programa detecta que la animación no es visible, le dice al thread de la animación que pare. Cuando el usuario revisita la animación, el programa rebpieza el thread a menos que el usuario haya pedido que se parara la animación.

> Podrías preguntarte por qué incrbentar el número de marco al principio del turno en vez al final. La razón para hacer esto es lo que sucede cuando el usuario congela la aplicación, la deja y luego la revisita. Cuando el usuario congela la animación, el bucle de animación se completa antes de salir. Si el número de marco se incrbentara al final del bucle, en lugar de al principio, el número de marco cuando el bucle sale sería uno más que el marco que se está mostando. Cuando el usuario revisita la animación, la animación podría ser congelada en un marco diferente del que dejó el usuario. Esto podría ser desconcertante y, si el usuario para la animación en un marco particular, aburrido.

Ozito