[Pong] II: Pelota cuadrada

Tengo una escena grabada en la memoria. Estaba yo en mi plena efervescencia adolescente y llegaba a casa del instituto con las notas en la mano. Digamos que no eran precisamente excelentes… vamos, me habían quedado siete. Traté de minimizar la gravedad del asunto destacando mi notable en religión. Mi madre me miró a los ojos y comenzó su discurso con un: “Tienes las pelotas cuadradas…”

No, mi madre no se refería al Pong ni a los gráficos tipo Atari, y desde luego que esta historia no viene a cuento, pero me ha hecho gracia y me apetecía contarlo.

Como decía en la anterior entrada, el paso que voy a dar hoy es construir la pelota que se moverá por nuestro recién creado tablero del Pong. También crearé su movimiento y, por supuesto, será cuadrada. Vamos a ello.

En primer lugar vamos a añadir dos atributos nuevos a la clase Configuración. Estos serán LADO_PELOTA velocidad. Importante el detalle: el tamaño de la pelota no variará en ningún momento, por lo que haremos que el atributo sea de tipo final, sin embargo, si que tengo en mente que la velocidad de la pelota (en milisegundos, hablaré de esto más tarde) pueda variar durante el juego, por lo que haremos que el atributo sea una variable con un valor de 3 por el momento y además de su método get le añadiremos un método set que podremos utilizar para alterarla desde otras clases. Con todo nos quedará tal que así:

package Configuracion;

/**
 * Configuración del tablero del Pong
 * Incluye los atributos finales X e Y que representarán la longitud del tablero
 * También incluye el tamaño de la pelota y su velocidad de movimiento en milisegundos
 * @author Phil Spectrum
 */
public class Configuracion
{
    private final int X = 500;
    private final int Y = 325;
    private final int LADO_PELOTA = 15;
    private int velocidad = 3;

    public Configuracion()
    {
    }

    public int getX()
    {
        return X;
    }

    public int getY()
    {
        return Y;
    }

    public int getLADO_PELOTA()
    {
        return LADO_PELOTA;
    }

    public int getVelocidad()
    {
        return velocidad;
    }

    public void setVelocidad(int velocidad)
    {
        this.velocidad = velocidad;
    }

}

Por cierto, os habréis dado cuenta de que el tamaño de la pelota ahora es de 15px y no de 25 como tenía pensado en un principio. Esto es porque al hacer algunas pruebas me pareció que era demasiado grande y decidí coger un número más bajo múltiplo de 5 también. Lo bueno de tener todos estos datos en una clase externa es que podremos alterar completamente el juego posteriormente con solo cambiar los atributos de la clase 🙂

Bien, ya estamos preparados para el obús, pero como es bien sabido que la programación es todo un deporte de riesgo, no nos quedaremos ahí. Vamos a crear la clase Pelota, con un par.

Para esta clase importaremos Color y Graphics2D, y también llamaremos a nuestra clase Configuracion creada anteriormente.

Crearemos las variables x e y que nos servirán para definir las coordenadas en las que aparecerá nuestra pelota cuadrada. También movimientoX y movimientoY que usaremos para definir como se alterarán estas mismas coordenadas en el método mover(). Y también creamos un atributo config para poder llamar a los métodos de la clase Configuracion.

A partir de aquí, la cuestión es definir los métodos. Primero creamos el constructor, al que le pasamos como parámetro la configuración, para que coja lo que hemos definido ahí. El siguiente método será dibujarPelota(), al que le pasamos como parámetro Graphics2D para utilizar fillRect() (porque no olvidemos que nuestra pelota será cuadrada) con nuestros atributos x e y y el tamaño definido en Configuracion.

Por último tenemos el método mover() en el que alteramos los valores de las variables moverX y moverY en función de en que coordenadas del frame se encuentre el gráfico de la pelota. Algo así:

*24/07/2013 Edito: Gracias a Eduardo Martinez me he percatado de un error muy tonto en esta parte del código. En la clase dibujar pelota() la selección de color debe hacerse antes de pintarla, por tanto el código debe quedar como lo pongo a continuación. Desde un punto de vista práctico es lógico: eliges el color antes de pintar la puerta de casa, no pintas la puerta y después eliges el color (¿o si?).

package Grafico;

import Configuracion.Configuracion;
import java.awt.Color;
import java.awt.Graphics2D;

/**
 * Clase Pelota
 * Incluye lo necesario para dibujarla y su movimiento en el frame
 * @author Phil Spectrum
 */
public class Pelota
{
    private int x = 0;
    private int y = 0;
    private int moverX = 1;
    private int moverY = 1;
    private Configuracion config;

    public Pelota(Configuracion config)
    {
        this.config = config;
    }

    /**
     * Método que especifica las caracterísitcas de la pelota para dibujarla
     * @param g le enviamos los gráficos a través de este parámetro
     */
    public void dibujarPelota(Graphics2D g)
    {
        g.setColor(Color.GRAY);
        g.fillRect(x, y, config.getLADO_PELOTA(), config.getLADO_PELOTA());
    }

    /**
     * Método mediante el que se mueve la pelota
     * Cuando esta llega a los bordes del frame altera la dirección de las coordenadas
     */
    public void mover()
    {
        if(x + moverX < 0)
        {
            moverX = 1;
        }
        else if (x + moverX > config.getX() - config.getLADO_PELOTA())
        {
            moverX = -1;
        }
        else if(y + moverY < 0)
        {
            moverY = 1;
        }
        //Aquí no se por qué hay que añadirle 25px más, si no se sale del frame
        else if(y + moverY > config.getY() - (config.getLADO_PELOTA()+25))
        {
            moverY = -1;
        }
        x = x + moverX;
        y = y + moverY;
    }
}

Por cierto, no tengo ni puta idea de por qué hay que sumarle 25 pixels más al caso de que la pelota llegue al borde Y, pero si no se los añadía se salía del frame, probarlo y lo comprobaréis. Si alguien encuentra una solución mejor y me la comenta le estaría profundamente agradecido.

Ahora viene donde la matan, y he de decir que parte de este código se lo debo a la ayuda de Juan Cabrerizo, así que le mando un saludo desde Australia. Se trata de la clase Pong, la principal. Necesitamos un método que nos pinte la maldita pelota en el frame y además cada x tiempo necesitamos que la borre y la repinte en otras coordenadas diferentes. Un coñazo vamos.

Para ello vamos a utilizar la clase Timer que iniciaremos desde el constructor de Pong. Su velocidad, definida en milisegundos la sacaremos de nuestra clase Configuración, con lo cual, cada 3 milisegundos lanzará un evento. Este será definido en el método actionPerformed() que creamos al final y desde el cual llamamos al método mover() de nuestra recién creada clase Pelota y también al método repaint(). Por supuesto, para que todo esto funcione hay que implementar ActionListener en la clase e importar un montón de mierdaca, pero no os preocupéis, esto NetBeans lo hace el solito (que listo el chaval).

Además, vamos a llamar al método paint() porque si no no tendremos pelota que valga. Desde el utilizamos super.paint(g) para limpiar la pantalla y Graphics2D para aplicar efectos de renderizado que suavicen las texturas además de llamar al método dibujarPelota() enviándole el parámetro de los gráficos. Con todo, el mamotreto que nos queda será así:

package Grafico;

import Configuracion.Configuracion;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
 * Clase Pong que incluye el método main desde donde se inicia el programa
 * La clase implementa ActionListener para trabajar con sus métodos
 * @author Phil Spectrum
 */
public class Pong extends JPanel implements ActionListener
{
    private Configuracion config = new Configuracion();
    private Pelota pelota = new Pelota(config);
    private Timer timer;

    //En el constructor iniciamos el Timer
    public Pong()
    {
        timer = new Timer(config.getVelocidad(), this);
        timer.start();
    }

    //Llamamos al método paint para dibujar los gráficos en el frame
    @Override
    public void paint(Graphics g)
    {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        pelota.dibujarPelota(g2d);
    }

    public static void main(String[] args)
    {
        JFrame frame = new JFrame("Pong!!");
        Configuracion config = new Configuracion();
        Pong pong = new Pong();
        pong.setBackground(Color.black);
        frame.add(pong);
        frame.setSize(config.getX(), config.getY());
        frame.setLocation(500, 150);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
    }

    /**
     * Aquí especificamos lo que va a hacer el Timer
     * En este caso, llama el método mover(); de la clase Pelota y repinta la pantalla
     * @param e le enviamos un evento, en este caso el Timer
     */
    @Override
    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource() == timer)
        {
            pelota.mover();
            repaint();
        }
    }
}

El resultado de todo esto será una pelota cuadrada gris que rebotará por todo nuestro frame. Mi madre estará orgullosa.

Pelota Cuadrada

Y con esto terminamos por hoy. Espero que os haya gustado, para la próxima continuaremos para bingo con un par de raquetas ^_^

Anuncios
Esta entrada fue publicada en Happy Coding y etiquetada , , , , . Guarda el enlace permanente.

4 respuestas a [Pong] II: Pelota cuadrada

  1. Edu. dijo:

    Por cierto,
    Ya que has optado por una clase Configuracion que permite configurar de manera cómoda, ¿por que no permites desde esta clase configurar el color de la pelota y el color de la cancha?

    • Me lo planteé al principio. Al final sin embargo no lo hice porque no buscaba que el color fuese una propiedad “editable”. En cualquier caso a nivel código si que es cierto que sería lo suyo 🙂

      Un saludín.

  2. Pingback: [Pong] III: Atasco temporal | The Phil Spectrum Project

  3. Pingback: [Pong] VI: ¡¡Rompiendo la barrera del sonido!! | The Phil Spectrum Project

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s