[Pong] V: El que mete saca

trabajo de mierdaMi nivel de actualizaciones ha sufrido una bajada en las últimas semanas. Sois pocos los que me seguís en este blog pero bien avenidos y por eso os quiero y os pido disculpas por el retraso. Nunca he sido especialmente lumbreras con los estudios y septiembre me mantiene algo ocupado. A pesar de ello estoy aprendiendo grandes cosas de mi asignatura de FOL (Formación y Orientación Laboral), es fascinante os lo aseguro. Aunque echo de menos los detalles técnicos acerca del derecho a ser cagado en el pecho a diario durante tu jornada laboral. Debe ser un derecho muy importante cuando tus supervisores se empeñan tanto en recordarte que puedes ejercerlo, especialmente en las compañías de televenta (hola queridos ex-jefes de Orange) que, por otro lado, son las únicas empresas que ofertan curro actualmente en esta nuestra querida Marca España.

A pesar de todo sigo intentando a duras penas actualizar semanalmente, así que, basta de historias y vamos a lo que vamos: continuar con el Pong.

Desde mi última actualización hasta hoy han cambiado unas cuantas cosas. El código empieza a ser ya un chocho de mil demonios, pero no se asuste nadie. He tratado de dejarlo lo más claro posible y creo que se entiende bastante bien.

Lo que voy a hacer es darle ya una funcionalidad al juego. Pondremos puntuaciones, añadiremos un gráfico para dividir en dos la pantalla, haremos que la pelota salga desde el centro y que cuando termine el juego se escriban un “WIN” y un “LOOSE”, rollo muy arcade.

En primer lugar, necesitaremos una variable puntuacion que será el tipo int. Esta variable la vamos a añadir a la clase Raqueta pues es esta clase la que representa al jugador. La iniciaremos a cero en el constructor y crearemos sus métodos getter y setter. También crearemos dos atributos nuevos: xP e yP, que representarán las coordenadas que van a ocupar los números de la puntuación y los añadiremos también como parámetros en el constructor de Raqueta. Por último creamos otro atributo de tipo Font, que iniciamos también en el constructor con el tipo de fuente que vamos a utilizar.

Creamos también los métodos dibujarPuntuacion() y dibujarGanador(). El primero nos servirá para colocar los gráficos del marcador y será colocado en la posición que hayamos definido en los atributos xP e yP. El segundo servirá para colocar las palabras “WIN” y “LOOSE” en la posición que definamos mediante sus parámetros xG, yG para el ganador y xL, yL para el perdedor. Comprobaréis también que he eliminado todos los setColor() porque me he dado cuenta de que tanto en esta clase como en la clase Pelota que veremos después no nos hacen falta. Ahí va el código:

package Grafico;

import Configuracion.Configuracion;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

/**
 * Clase Raqueta que definirá las pautas para poder crear los dos objetos de
 * este tipo.
 * @author Phil Spectrum
 */
public class Raqueta 
{
    private int x;
    private int y;
    private int ya;  
    private int arriba;
    private int abajo;
    private Configuracion config;
    private boolean subiendo, bajando;
    private int puntuacion;
    private int xP;
    private int yP;
    private Font fuente;

    /**
     * En su constructor pedimos los valores para los atributos que necesitaremos
     * En el caso de "x", "y", "arriba" y "abajo" serán diferentes para cada jugador
     * Iniciamos también en false los atributos subiendo y bajando,
     * la puntuación a 0 y escogemos la fuente que utilizaremos en la aplicación
     * @param x la posición x que ocupará la raqueta en el frame
     * @param y la posición y que ocupará la raqueta en el frame
     * @param arriba el código de la tecla que hará que la raqueta se mueva hacia arriba 
     * @param abajo el código de la tecla que hará que la raqueta se mueva hacia abajo
     * @param config llamada a la clase Configuración donde se define el tamaño
     * @param xP coordenada x donde se dibujará la puntuación del jugador
     * @param yP coordenada y donde se dibujará la puntuación del jugador
     */
    public Raqueta(int x, int y, int arriba, int abajo, Configuracion config, int xP, int yP) 
    {
        this.fuente = new Font("Arial", Font.BOLD, 20);
        this.xP = xP;
        this.yP = yP;
        this.puntuacion = 0;
        this.ya = 1;
        this.subiendo = false;
        this.bajando = false;
        this.x = x;
        this.y = y;
        this.arriba = arriba;
        this.abajo = abajo;
        this.config = config;
    }

    /**
     * Método que controla el movimiento de la raqueta
     * Permite sumar o restar el valor del atributo "ya" al de "y" siempre y cuando
     * la raqueta no haya pasado de los límites del frame y los atributos
     * "bajando" o "subiendo" tengan valor verdadero.
     */
    public void mover() 
    {
        //Aquí resto 28 pixels igual que los 25 de la clase pelota
        if (((y + ya >= 0) & (y - ya < (config.getY() - 28) - config.getLARGO_RAQUETA()) & bajando))
        {
            y = y + ya;
        }
        else if (((y + ya) > 0) && ((y - ya) <= ((config.getY() - 28) - config.getLARGO_RAQUETA())) && subiendo)
        {
            y = y - ya;
        }
    }
    
    /**
     * Método para dibujar la raqueta
     * @param g le envia los gráficos
     */
    public void dibujarRaqueta(Graphics2D g) 
    {
        g.fillRect(x, y, config.getANCHO_RAQUETA(), config.getLARGO_RAQUETA());
    }
    
    /**
     * Con este método, cuando el usuario levanta la tecla el movimiento se para
     * ya que a la variable que lo controla se le da el valor de false
     * @param e captura un evento de teclado
     */
    public void keyReleased(KeyEvent e) 
    {
        if (e.getKeyCode() == arriba)
        {
            subiendo = false;
        }
        else if (e.getKeyCode() == abajo)
        {
            bajando = false;
        }
    }
    
    /**
     * Con keyPressed indicamos lo que queremos hacer cuando se pulsa una tecla
     * En este caso, las teclas irán definidas por los atributos "arriba" y "abajo"
     */
    public void keyPressed(KeyEvent e) 
    {
        if (e.getKeyCode() == arriba)
        {
            subiendo = true;
        }
        else if (e.getKeyCode() == abajo)
        {
            bajando = true;
        }
    }   

    public int getX() 
    {
        return x;
    }

    public int getY() 
    {
        return y;
    }
    
    /**
     * Con este método crearemos un rectángulo alrededor de la raqueta que podremos usar
     * para comprobar las colisiones
     * @return el rectángulo
     */
    public Rectangle getBounds()
    {
        return new Rectangle(x, y, config.getANCHO_RAQUETA(), config.getLARGO_RAQUETA());
    }

    /**
     * Método que dibuja las puntuaciones
     * @param g gráficos
     */
    public void dibujarPuntuacion(Graphics2D g)
    {
        g.setFont(fuente);
        g.drawString(String.valueOf(getPuntuacion()), xP, yP);
    }
    
    /**
     * Método para dibujar las palabras "WIN" y "LOOSE" cuando finaliza el juego
     * @param g gráficos
     * @param xG coordenada x del ganador
     * @param yG coordenada y del ganador
     * @param xL coordenada x del perdedor
     * @param yL coordenada y del perdedor
     */
    public void dibujarGanador(Graphics2D g, int xG, int yG, int xL, int yL)
    {
        g.setFont(fuente);
        g.drawString("WIN!!", xG, yG);
        g.drawString("LOOSE", xL, yL);
    }
    
    public int getPuntuacion() 
    {
        return puntuacion;
    }

    public void setPuntuacion(int puntuacion) 
    {
        this.puntuacion = puntuacion;
    }
}

Vamos ahora con la clase Pelota.

Para poder subir puntuaciones, necesitamos que la pelota pueda salir del frame, por lo tanto, lo primero que vamos a hacer es eliminar las colisiones con los bordes x en el método mover(). También hemos alterado los valores de x e para hacer que cada objeto de este tipo salga desde el centro. Y con esto y un bizcocho, clase Pelota terminada:

package Grafico;

import Configuracion.Configuracion;
import java.awt.Graphics2D;
import java.awt.Rectangle;

/**
 * Clase Pelota
 * Incluye lo necesario para dibujarla y su movimiento en el frame
 * @author Phil Spectrum
 */
public class Pelota
{
    private int x = 239;
    private int y = 135;
    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.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(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;
    }

    public Rectangle getBounds()
    {
        return new Rectangle(x, y, config.getLADO_PELOTA(), config.getLADO_PELOTA());
    }

    public void setMoverX(int moverX)
    {
        this.moverX = moverX;
    }

    public int getX()
    {
        return x;
    }
}

Vamos ahora con el bacalao gordo, la clase Pong, donde he hecho todo lo inimaginable.

En primer lugar añadimos a jugadorA y jugadorB los nuevos parámetros xP e yP que ahora nos requieren, para colocar las puntuaciones en su sitio.

metodo reglamentario

En el método paint() de esta clase será donde añadamos el setColor() que será el color por defecto que le daremos a todos los objetos. También dibujaremos una recta que dividirá el frame en dos y hará que el juego tenga algo más de gracia, para ello usaremos fillRect() y para medir exactamente el centro, yo he utilizado el llamado método reglamentario. Este método requiere de gran destreza y amplia sabiduría informática, y su aplicación podéis verla en el gráfico que adjunto a la derecha. Advierto que es solo para auténticos expertos.

Además de esto, continuaremos para bingo haciendo las llamadas pertinentes a los métodos dibujarPuntuacion() (el que hemos  creado antes) desde jugadorA y jugadorB. Vamos a poner un límite de 10 puntos, así que añadiremos un if para que cuando alguna de las puntuaciones sea igual a 10, se dibujen las palabras WIN y LOOSE en las posiciones del frame que correspondan a cada caso. Esto lo hacemos con la llamada al método dibujarGanador().

Por último nos vamos al método actionPerformed() donde vamos a especificar que queremos que el programa haga cuando la pelota sale del frame. En resumen, lo que buscamos es que cuando esto ocurra se sume un punto a la puntuación del último jugador que ha golpeado la pelota y que tras un tiempo prudencial se genere una nueva pelota que vaya en la dirección del jugador que ha marcado. Es lo que en argot informático se llama: el que mete saca, y así hasta 10 (no vayamos a pasarnos).

Para conseguir que la nueva pelota salga tras un tiempo, supongo que lo más correcto sería parar el timer durante un número determinado de segundos, lo cual creo que se hace con el método wait() pero, como no he sabido como implementarlo, he hecho una pequeña chapuza (si a alguien se le ocurre una mejor manera, los comentarios están como siempre abiertos) que es dejar que la pelota siga moviéndose fuera del frame. Así, hasta que la pelota no llega hasta el final del frame más 150 píxeles extra no se genera una nueva pelota. Esto por supuesto se hará siempre que la puntuación sea menor que 10 y además, en el caso de que quien haya marcado sea el jugadorA, haremos con setMoverX(-1) que la nueva pelota vaya hacia el. Finalmente pararemos el timer cuando alguna de las dos puntuaciones sean igual a 10 y creamos un nuevo if para llamar a todos los métodos mover() y repintar la pantalla con repaint() siempre que la puntuación sea menor o igual a 10. ¡Código va!

package Grafico;

import Configuracion.Configuracion;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
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 Raqueta jugadorA = new Raqueta(25, 125, 87, 83, config, 210, 30);
    private Raqueta jugadorB = new Raqueta(460, 125, 38, 40, config, 270, 30);
    private Timer timer;

    //En el constructor iniciamos el Timer y el Listener del teclado
    public Pong()
    {
        timer = new Timer(config.getVelocidad(), this);
        timer.start();
        addKeyListener(new KeyListener()
        {
                @Override
                public void keyTyped(KeyEvent e)
                {
                }

                @Override
                public void keyReleased(KeyEvent e)
                {
                        jugadorA.keyReleased(e);
                        jugadorB.keyReleased(e);
                }

                @Override
                public void keyPressed(KeyEvent e)
                {
                        jugadorA.keyPressed(e);
                        jugadorB.keyPressed(e);
                }
            });
        setFocusable(true);
    }

    /**
     * Método que llama al método mover() de Raqueta
     */
    private void mover()
    {
        jugadorA.mover();
        jugadorB.mover();
    }

    /** Llamamos al método paint para dibujar los gráficos en el frame
     * Elegimos con setColor el color que llevará todo
     * Dentro llamamos a los métodos de dibujo de cada cosa que queremos añadir
     * Dibujamos además una recta que divide en dos partes el tablero
     */
    @Override
    public void paint(Graphics g)
    {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(Color.GRAY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.fillRect(244, 0, 5, 325);
        pelota.dibujarPelota(g2d);
        jugadorA.dibujarRaqueta(g2d);
        jugadorB.dibujarRaqueta(g2d);
        jugadorA.dibujarPuntuacion(g2d);
        jugadorB.dibujarPuntuacion(g2d);
        if (jugadorA.getPuntuacion()==10)
        {
            jugadorA.dibujarGanador(g2d, 100, 150, 330, 150);
        }
        else if (jugadorB.getPuntuacion()==10)
        {
            jugadorB.dibujarGanador(g2d, 340, 150, 90, 150);
        }

    }

    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
     * También llama al método mover() de la misma clase Pong
     * Controlamos las colisiones con intersects y
     * alteramos la puntuación cada vez que la pelota sale del frame y
     * creamos una nueva pelota si esta es menor que 10
     * En el momento en que la puntuación llega a 10, paramos el timer
     * @param e le enviamos un evento, en este caso el Timer
     */
    @Override
    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource() == timer)
        {
            Rectangle rA = jugadorA.getBounds();
            Rectangle rB = jugadorB.getBounds();
            Rectangle rP = pelota.getBounds();
            if (rA.intersects(rP))
            {
                pelota.setMoverX(1);
            }
            else if (rB.intersects(rP))
            {
                pelota.setMoverX(-1);
            }
            else if (pelota.getX() > (config.getX() + 150))
            {
                jugadorA.setPuntuacion(jugadorA.getPuntuacion()+1);
                if (jugadorA.getPuntuacion() < 10)
                {
                    pelota = new Pelota(config);
                    pelota.setMoverX(-1);
                }
            }
            else if (pelota.getX() < - 150)
            {
                jugadorB.setPuntuacion(jugadorB.getPuntuacion() + 1);
                if (jugadorB.getPuntuacion() < 10)
                {
                    pelota = new Pelota (config);
                }
            }
            else if (jugadorA.getPuntuacion() == 10 | jugadorB.getPuntuacion() == 10)
            {
                timer.stop();
            }
            if (jugadorA.getPuntuacion()<=10 & jugadorB.getPuntuacion()<=10)
            {
                pelota.mover();
                this.mover();
                this.repaint();
            }
        }
    }
}

La clase Configuracion se mantendrá sin cambios, tal y como lo dejé en las anteriores actualizaciones. Con todo, esto nos debería de quedar así de bonico:

pong con puntuaciones

Espero que os haya gustado, ahora ya os podéis ir echando alguna partida mientras yo voy preparando los sonidos para la próxima actualización. Salud y happy coding! amigos.

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

10 respuestas a [Pong] V: El que mete saca

  1. Edu. dijo:

    Bueno, está claro cual es el siguiente paso ¿no?…. 1 ó 2 jugadores…

    • No pensaba hacer una IA, simplemente dejarlo para 2 jugadores sin más. Aunque el hecho de hacer una IA me parece interesante de cara a aprender como funciona.

      De todas formas (y esto es un poco filosofía mía) me gustan que los juegos fomenten la interacción entre personas. Me encantan los juegos de mesa y su planteamiento de reunirse con unos amigos y pasar un rato divertido. Por eso el tipo de ideas de juegos que tengo en mente, van un poco en ese plan (lo que no quiere decir que descarte otras cosas)

      Un saludín!

      • Edu dijo:

        A mi también me llaman mas los juegos que fomenten la reunión y diversion… pero a quien no le apetece alguna vez “jugar contra la máquina” como decíamos cuando jugábamos en las recreativas.
        En este caso en concreto creo que la IA de la “máquina” no sería demasiado compleja. A priori se me ocurren dos opciones:
        La primera es que la raqueta suba o baje en función de la posición en el eje “Y” de la pelota.
        La segunda es utlizando “nuestras amigas la matemátcas”, una vez que sale la pelota rebotada de la raqueta del jugador 1 ya se puede calcular el punto de impacto en el otro lado (trigonometría pura).
        En función de la velocidad de desplazamiento de la raqueta de la máquina, su efectividad sería mayor o menor (ya entramos en niveles de dificultad…).
        También podemos determinar de manera aleatoria en que parte de la raqueta golpear para que la máquina no de dedique solo a “devolverlas” y tenga “variedad de golpes”…
        Seguro que hay alguna forma más de hacerlo y mas variables a introducir…
        Bueno, siempre te lo puedes plantear para cuando hayas terminado el de 2 jugarores.

        Saludos,

  2. Edu dijo:

    Por cierto, sería bueno poder editar los mensajes una vez enviados. Siempre encuentro alguna falta o error cuando ya le he dado al botoncito.

  3. Edu, creo que eso que comentas lo puedes hacer cuando eres usuario registrado de WordPress, aunque no me hagas mucho caso. También creo que hay una opción para registrarse como usuario del blog, ya te digo que no estoy seguro pero mañana lo miro a ver 🙂

    Respecto a lo de la IA, efectivamente no sería algo muy complejo para este juego en si. Lo echaré un vistazo cuando le de por definitivo ^_^

    Un saludo!

  4. Juan Cabrerizo dijo:

    Me mola como va. ¿vas a sacar la versión de Android? ¿y para Windows RT? Creo que me he comprado una surface así que lo puedes ir migrando 😀

  5. Robertus dijo:

    WIN & LOSE.

    Si pones LOOSE igual el juego se afloja un poco! 😛

  6. 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