## 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 implements Runnable {

        //In initialization code:
            //From user-specified frames-per-second value, determine
    	//how long to delay between frames.

        //In a method that does nothing but start the animation:
            //Create and start the animating thread.

        //In a method that does nothing but stop the animation:
            //Stop the animating thread.

        public boolean mouseDown(Event e, int x, int y) {
            if (/* animation is currently frozen */) {
                //Call the method that starts the animation.
            } else {
                //Call the method that stops the animation.
            }
        }

        public void run() {
            //Lower this thread's priority so it can't interfere
    	//with other processing going on.

            //Remember the starting time.

            //Here's the animation loop:
            while (/* animation thread is still running */) {
                //Advance the animation frame.
                //Display it.
                //Delay depending on how far we are behind.
            }
        }

        public void paint(Graphics g) {
            //Draw the current frame of animation.
        }
    }

#### 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 ejemplar (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í está el código que realiza estas tareas:

>     while (/* animation thread is still running */) {
>         //Advance the animation frame.
>         frameNumber++;

>         //Display it.
>         repaint();

>         ...//Delay depending on how far we are behind.
>     }

#### 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 embargo, hace que el thread duerma demasiado, ya que ha perdido cierto tiempo 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 implementa esto:

>     long startTime = System.currentTimeMillis();
>     while (/* animation thread is still running */) {
>         ...//Advance the animation frame and display it.
>         try {
>             startTime += delay;
>             Thread.sleep(Math.max(0,
>                                   startTime-System.currentTimeMillis()));
>         } 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á implementada 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 implementa esto:

>     ...//In initialization code:
>     boolean frozen = false;

>     ...//In the method that starts the animation thread:
>         if (frozen) {
>             //Do nothing.  The user has requested that we
>             //stop changing the image.
>         } else {
>             //Start animating!
>            ...//Create and start the animating thread.
>         }
>     }

>     . . .

>     public boolean mouseDown(Event e, int x, int y) {
>         if (frozen) {
>             frozen = false;
>             //Call the method that stops the animation.
>         } else {
>             frozen = true;
>             //Call the method that stops the animation.
>         }
>         return true;
>     }

> La segunda caracteristica es suspender la animación siempre que el applet o la aplicación no sean visibles. Para la plantilla de animación de applet, esto se consigue implementando los métodos stop() y start() del applet. Para la plantilla de la apliación, esto se consigue implementando 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 reempieza el thread a menos que el usuario haya pedido que se parara la animación.

> Podrías preguntarte por qué incrementar 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 incrementara 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