domingo, 16 de junio de 2013

Implementación de un juego utilizando la clase Timer en Java

La Clase Timer es utilizada en Java para lanzar eventos a intervalos específicos de tiempo, de manera repetitiva. Son muy usados para las animaciones y movimientos de objetos en los juegos de computadora. La utilización de un Timer involucra dos cosas: Indicar el intervalo de tiempo dentro del cual se repetirá determinada acción y registrar un Oyente o Listener quien será el encargado de ejecutar la acción que queremos sea repetida. Un ejemplo muy sencillo es el clásico reloj en las aplicaciones SWING que muestro en la siguiente clase y en donde se indica que cada 500 milisegundos actualizemos el tiempo que es mostrado en un JLabel:
package com.rolandopalermo.blog.juego;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.Timer;

/**
 *
 * @author Rolando
 */
public class VentanaPrueba extends javax.swing.JFrame {

    /** Creates new form VentanaPrueba */
    public VentanaPrueba() {
        initComponents();
        setSize(160, 60);
        setLocationRelativeTo(null);
        final SimpleDateFormat formatoDeFecha = new SimpleDateFormat("HH:MM:ss");
        Timer reloj = new Timer(500, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                lblTiempo.setText(formatoDeFecha.format(new Date()));
            }
        });
        reloj.start();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        lblTiempo = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        getContentPane().add(lblTiempo, java.awt.BorderLayout.CENTER);

        pack();
    }// </editor-fold>

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(VentanaPrueba.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(VentanaPrueba.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(VentanaPrueba.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(VentanaPrueba.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new VentanaPrueba().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JLabel lblTiempo;
    // End of variables declaration
}
Ahora vayamos un poco más allá e implementemos un sencillo juego en unos cuantos minutos con la ayuda de la clase Timer. La lógica es sencilla y paso a describirla:

El juego debe consistir en tratar de matar ratas que deberán aparecer en posiciones aleatorias utilizando el mouse.

Lo primero que tenemos que hacer es crear una clase que represente al elemento principal del juego, en este caso: Una Rata.
package com.rolandopalermo.blog.juego;

import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import javax.swing.ImageIcon;

/**
 *
 * @author Rolando
 */
public class Rata {

    private int x;
    private int y;
    private boolean estaViva;

    public Rata() {
        this.estaViva = true;
        this.x = 0;
        this.y = 0;
    }

    public void actualizarPosicion(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public boolean isEstaViva() {
        return estaViva;
    }

    public void setEstaViva(boolean estaViva) {
        this.estaViva = estaViva;
    }

    public void dibujar(Graphics g) {
        if (estaViva) {
            g.drawImage(new ImageIcon(getClass().getResource("/com/rolandopalermo/blog/juego/rat.png")).getImage(), x, y, null);
        }
    }

    boolean estaDentro(int xx, int yy) {
        Rectangle r = new Rectangle(x, y, 64, 64);
        return r.contains(new Point(xx, yy));
    }
}
Ahora necesitamos implementar la clase que se encargará de gestionar el escenario en donde se desarrollará el juego. Para esto vamos a reescribir el método paintComponent de un JPanel.
package com.rolandopalermo.blog.juego;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

/**
 *
 * @author Rolando
 */
public class Escenario extends JPanel implements MouseListener, MouseMotionListener {

    private List<Rata> ratas;
    private Image foot;
    private Image fondo;
    private int xfoot = 0;
    private int yfoot = 0;

    public Escenario(List<Rata> esferas) {
        this.ratas = esferas;
        foot = new ImageIcon(getClass().getResource("/com/rolandopalermo/blog/imagenes/foot.png")).getImage();
        fondo = new ImageIcon(getClass().getResource("/com/rolandopalermo/blog/imagenes/fondo.jpg")).getImage();
        addMouseListener(this);
        addMouseMotionListener(this);
    }

    @Override
    public void paintComponent(Graphics g) {
        int w = this.getWidth();
        int h = this.getHeight();
        g.setColor(Color.WHITE);
        g.drawImage(fondo, 0, 0, null);
        if (ratas != null) {
            for (Rata esfera : ratas) {
                esfera.dibujar(g);
            }
        }
        g.drawImage(foot, xfoot, yfoot, null);
    }

    public void actualizarPosiciones() {
        for (Rata rata : ratas) {
            rata.actualizarPosicion(generarAleatorio(100, 400), generarAleatorio(100, 400));
        }
    }

    private int generarAleatorio(int a, int b) {
        return (int) Math.floor(Math.random() * (b - a + 1) + a);
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        xfoot = e.getX() - 32;
        yfoot = e.getY() - 32;
        repaint();
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        for (Rata rata : ratas) {
            if (rata.estaDentro(e.getX(), e.getY())) {
                rata.setEstaViva(false);
                repaint();
                break;
            }
        }
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
}
Y con esto habremos finalizado el desarrollo de nuestro juego, evidentemente faltan muchas cosas básicas, pero la idea es mostrarles lo sencillo que es empezar con esta interesante Clase de Java que puede sacarnos de muchos apuros.


Y para finalizar les dejo el código fuente del programa esperando que les sea de utilidad y les ayude a comprender el uso de la clase Timer.

Descargar 
Hasta una próxima oportunidad y no se olviden de seguir a este blog a través de su página en Facebook.