Single Tech Games

Capitulo 2. An Animation Framework – parte 2

2) Double Buffering Drawing
gameRender() dibuja su propio objeto Graphics, que representa una imagen del mismo tamaño que la pantalla (dbImage)

// Variables globales para el renderizado fuera de pantalla
private Graphics dbg;
private Image dbImage = null;
private void gameRender()
//dibuja el frame actual en un buffer de imagen
{
    if (dbImage == null){ // crea el buffer
        dbImage = createImage(PWIDTH, PHEIGHT);
        if (dbImage == null) {
            System.out.println("dbImage es nulo");
        return;
        }
        else
            dbg = dbImage.getGraphics();
    }
// limpia el background
    dbg.setColor(Color.white);
    dbg.fillRect (0, 0, PWIDTH, PHEIGHT);
    // dibuja los elementos del juego
    if (gameOver)
        gameOverMessage(dbg);
} // fin de gameRender()
private void gameOverMessage(Graphics g)
// centra el mensaje de game-over
{
    g.drawString(msg, x, y);
} // fin de gameOverMessage()

El doble buffer, como ya sabemos, lo que hace es dibujar las operaciones requeridas para la renderizacion pero en una imagen alterna, no directamente en la pantalla

paintComponent() es la función que dibuja, es llamado en el bucle de run() y va después de la renderizacion

public void paintComponent(Graphics g)
{
    super.paintComponent(g);
    if (dbImage != null)
        g.drawImage(dbImage, 0, 0, null);
}

La razón o ventaja de usar el doble buffer es que si se dibuja directamente en la pantalla, se hace lento y el usuario lo va a anotar,  drawImage() es lo suficientemente rápido para pasar desapercibido
paintComponent() se debe mantener simple porque muchas veces la maquina virtual de java lo necesita fuera de su funcionamiento en el hilo de la animación, por ejemplo cuando se pone otra ventana(programa) por delante de ella
No cometan el error de poner el comportamiento del juego (IA) dentro de la función paintComponent() porque sino el juego continuaría a pesar de que la ventana no se este utilizando
3) Adding User Interaction
Ahora veremos como se monitorea el teclado y el mouse.
GamePanel() utiliza el teclado para cambiar su variable boolean a falsa, y así termina el bucle de animación y la aplicación, la función testPress() servirá para procesar los eventos del mouse

public GamePanel()
{
    setBackground(Color.white);
    setPreferredSize( new Dimension(PWIDTH, PHEIGHT));
    setFocusable(true);
    requestFocus(); // JPanel ahora recibe eventos de teclado
    readyForTermination();
    // crear los componentes de un juego
    // escucha los eventos del mouse
    addMouseListener( new MouseAdapter() {
public void mousePressed(MouseEvent e)
{ testPress(e.getX(), e.getY()); }
});
} // Fin de GamePanel()

readyForTermination() es la función de monitoreo de el teclado y testares() del mouse

private void readyForTermination()
{
    addKeyListener( new KeyAdapter() {
// Escucha a esc, q, fin, ctrl + c
    public void keyPressed(KeyEvent e)
    {
        int keyCode = e.getKeyCode();
        if ((keyCode == KeyEvent.VK_ESCAPE) ||
        (keyCode == KeyEvent.VK_Q) ||
        (keyCode == KeyEvent.VK_END) ||
        ((keyCode == KeyEvent.VK_C) && e.isControlDown()) ) {
            running = false;
        }
    }
    });
} // fin de readyForTermination()
private void testPress(int x, int y)
// es (x,y) importante para el juego?
{
    if (!gameOver) {
    // hace algo
    }
}

4. Active Rendering
Como repaint() es solo un pedido es difícil saber cuando acaba, era el problema que vimos antes, hasta ahora siempre adivinamos tiempo de la siesta, si lo hacemos muy larga, estamos haciendo el juego lentos por las puras, y si lo hacemos muy corto el juego puede saltera los frames, pero la verdad es que adivinar el tiempo de la siesta no es la mejor opción, ya que el tiempo que toma renderizar y actualizar varia dependiendo de la actividad que haya en el juego, entonces habrá que idear una solución

public void run()
/* actualiza, renderiza y duerme constantemente*/
{
    running = true;
    while(running) {
        gameUpdate(); // Actualiza el estado del juego
        gameRender(); // renderiza en un buffer
        paintScreen(); // dibuja el buffer en la pantalla
        try {Thread.sleep(20); // pequeña siesta
        }
        catch(InterruptedException ex){}
    }
    System.exit(0); // cierra JFrame o JApplet si existen
} // fin de run()
private void paintScreen()
// constantemente renderiza la imagen del buffer en la pantalla
{
    Graphics g;
    try {
        g = this.getGraphics(); // trae el contexto grafico del panel
    if ((g != null) && (dbImage != null))
        g.drawImage(dbImage, 0, 0, null);
        g.dispose();
    }
    catch (Exception e)
    {   System.out.println("Error del contexto grafico: " + e); }
} // fin de paintScreen()

Con estos cambios ahora el renderizado pasa a nuestras manos y el tiempo puede ser medido y las preocupaciones de los repaint ya no existirán o serán menores
Explicando un poco el código la razón por la que usamos this.getGraphics(); es porque la maquina virtual cambia el contexto gráfico del panel cuando lo minimizan o ponen un programa por delante, es por eso que debe ser obtenido siempre que se necesite la función, por ejemplo una excepción sería cuando en un applet cierran la pagina web.

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments