26 marzo 2015

Detección de colisiones y escuchando al teclado

Hemos estado trabajando con un proyecto en Java que dibuja una pista de carreras (no muy estético, pero funcional) y luego usamos threads para mover 2 carros.  Hasta allí todo bien, pero no muy divertido que digamos.  Por eso, en este post, vamos a hacer dos cosas: detectar colisiones (para que los "carros" no puedan pasar por los obstáculos ni por encima del otro carro) y escuchar eventos del teclado para poder controlar los carros.  En pocas palabras, nuestro juego por fin va a ser funcional.

Pero como dijo Jack el destripador, vámonos por partes: primero vamos a ver que onda con la detección de colisiones y luego veremos que onda con  el teclado.  Así que sin más preámbulos, arrancamos...

Colisiones
Hacer la detección de colisiones (en otras palabras, ver si un objeto "choca" con otro) es bastante sencillo en Java cuando se usan objetos de la clase Rectangle (por eso los usamos desde el primer momento: era más sencillo dibujar sin ellos, pero nos sirven en este momento) porque tiene un método que revisa si hay colisión. Este método se llama intersects.

Digamos que quiero saber si el Rectangle r1 se intersecta (o colisiona) con un Rectangle r2, puedo usar el siguiente código:

if (r1.intersects(r2)) {
    // Código que se ejecuta si hay colisión
}

Y como podrán imaginar, para revisar múltiples colisiones, puedo usar el operador booleano AND (representado como &&) para ver si dos condiciones se cumplen o el operados OR (representado como ||) para ver si uno de los dos condiciones son verdaderos.

Por ejemplo, si quiero ver si el rectángulo r1 chocó con el rectángulo r2 o el r3, usaría este código:

if (r1.intersects(r2) || r1.intersects(r3)) {
    // Código que se ejecuta si choca con r2 o con r3
}

Esta imagen muestra si hay colisiones entre rectángulos (en otras palabras, si se intersectan):


Escuchando el teclado (KeyListener)
Ya en un post anterior hablé sobre "escuchar" eventos en Java (incluyendo el teclado), pero aquí me concentraré solo en el teclado para completar este proyecto.  Si desea profundizar, sugiero que vea el post Interfaces gráficas 4: Escuchando eventos.

El KeyListener permite recibir lo que ha escrito el usuario en el teclado.  Para usarlo, es necesario agregar, al final de la línea de "public class ..." lo siguiente (esto también aplica para clases internas como las que hicimos en los threads del post anterior):

implements KeyListener

Después de esto, en el constructor (o el método run si estás en un thread), hay que agregar el siguiente código para "despertar" al KeyListener y ponerlo a trabajar:

addKeyListener(this);

Recuerda que el this asocia los eventos que "escucha" del teclado a esta clase.

Después de esto, hay que agregar 3 métodos a la clase

public void keyPressed(KeyEvent e) { }
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }

Estas funciones, al igual que el paint del que hablamos en el primer post de esta serie, son ejecutadas de forma automática: el keyPressed se llama cuando se presiona una tecla, el keyReleased se ejecuta cuando el usuario quita el dedo de la tecla y ésta se levanta y el keyTyped ocurre cuando el usuario presiona y suelta la tecla.  El mas usado de los tres es el keyTyped, que es precisamente el único que vamos a codificar en este ejemplo.

Finalmente, para saber qué tecla presionó el usuario, uso el código:

e.getKeyChar()

(Si te fijas, e es el parámetro que viene en los métodos y es objeto de la clase KeyEvent que tiene información sobre lo que se ha tecleado).

Integrando este rollo con el proyecto
Como explico con más detalle en el video, voy a crear variables para indicar la dirección del carrito: si vale 0 el carrito viaja hacia arriba, 1 a la izquierda, etc.  Para hacerlo más fácil, los voy a hacer constantes porque no quiero que se modifiquen en el código (usando la palabra final al declararlos).

Después de esto, voy a usar el KeyListener para cambiar la dirección del carrito del jugador.  También vamos a hacer que el rumbo del carrito cambie al alterar la coordenada x si se mueve hacia la izquierda o derecha y la coordenada y si va hacia arriba o abajo.

Finalmente hay que revisar si el usuario no chocó con un obstáculo o con el otro jugador.  Si sucede, vamos a poner la velocidad del jugador en un valor negativo para que el carro retroceda hasta "agarrar vuelo".

Aquí les dejo el video donde lo explico y el código fuente completo (bueno, completo hasta este momento) lo pueden descargar aquí.


Agradecimientos
La imagen de Duke evitando colisiones en patineta lo tomé del blog de CPU y la de los rectángulos que se intersectan son de una página de Tufts University.  Y como ha sucedido con los 2 posts anteriores, la idea vino de este libro:



¡Hasta la próxima!

24 marzo 2015

Hilos en Java: Threads

En mi post anterior dibujamos una pista de carreras rudimentaria con rectángulos usando Java.  Pero, ¿de qué sirve una pista sin carros?  Eso mismo pensé, así que en esta ocasión vamos a añadirle código al proyecto anterior para que podamos tener dos carritos moviéndose de forma independiente una de otra.

La mejor manera de hacer que cada carro se mueva de forma independiente a la ejecución del resto del programa es usar algo llamaos hilos o threads como se conocen en inglés.  Cada thread es un código (o "flujo de control" para que se oiga más interesante) que se ejecuta en paralelo con el resto del programa (o con otros threads).  Es la forma de implementar multiprocesamiento en un programa de Java (¿nunca han escuchado de multithreading cuando hablan de sistemas operativos?).

Para crear un thread, lo vamos a implemtar como una clase que está dentro de la clase principal (en el caso de este ejemplo, va a ser una clase dentro de mi clase RaceGame).  Lo único que hay que cuidar es que herede de la clase Thread.  El código de declaración de la clase interna sería esta:

public class extends Thread {
}

Como has de imaginar, puede ser cualquier cosa menos el nombre de la clase en la que está declarado (recuérdate que lo estoy implementando como una clase dentro de la otra clase).  En mi caso, declaré dos clases de este tipo (una para cada carrito) llamados moveCar1 y moveCar2.

El corazón de un thread es el método run().  Aquí es donde se define el código que se va a ejecutar.  En mi caso concreto voy a mover un carro y lo voy a poner dentro de un ciclo infinito para que siempre esté moviéndose.  Un ciclo while(true) me sirve para este fin.

Para hacer mi programa mas seguro, voy a poner mi código dentro de un bloque try...catch.  El código que ponga después de la instrucción try se va a ejecutar, pero si hay algún problema va a hacer lo que le diga en la sección catch.  De otra forma, mostraría error y truena mi programa.  Lo que he dicho hasta ahorita se vería así:

while (true) {
    try {
        // código para mover el carrito
    } catch (Exception e) {
        break;
    }
}

Como podrá observar el lector, si sucede un error simplemente lo ignoro (con el break).  A lo mejor en otro tipo de programa podría poner código aquí para cerrar bases de datos, liberar memoria, o intentar otra cosa.  Pero en este programa simplemente voy a hacer de cuenta que nada pasó.  Y como el lector podrá deducir, en lugar de lo que va en el comentario // código para mover el carrito debe ir el código que haga que se mueva el carro.

Finalmente para que el Thread se ejecute, es necesario crear el objeto y llamar su método start().  Así que en el constructor de la clase principal, en mi caso RaceGame, voy a escribir lo siguiente:

moveCar1 mc1 = new moveCar1();
mc1.start();

En este video muestro lo que expliqué aquí (con los cambios que realicé en el código del proyecto anterior) y el código modificado lo puedes descargar aquí.


La imagen de Duke (la mascota de Java) con hilos (o Threads) lo tomé de una página de crunchify.com.  Y, al igual que con el post anterior, la idea de este proyecto surgió de este libro:



¡Hasta la próxima!

23 marzo 2015

Dibujando con Java: JFrames

Hablando de Java, ya había comentado como hacer interfaces gráficas, usar Layout Managers para ventanas, hacer interfaces complejas, escuchar eventos y hasta como usar un ScrollPane.  En esta ocasión voy a hablar de la forma de usar la clase JFrame para dibujar rectángulos en una ventana.  Obviamente estos principios pueden ayudarte a dibujar lo que quieras, pero voy a crear una pista de carreras rudimentaria para un juego donde los jugadores podrán controlar unos carritos (rectángulos) a través de esta pista.

Antes de mostrar el video, quiero resaltar algunos aspectos importantes del programa (el código fuente se puede descargar también más adelante).

Antes de hacer otra cosa, para dibujar en un JFrame (el JFrame es la pura ventana vacía), se tiene que incluir en la línea de declaración de la clase.  En mi caso concreto, se hace de esta manera:

public class RaceGame extends JFrame

Después de esto, hay que cambiar la función main() para que contenga una línea de código donde se crea el objeto de la clase (lo cual hace que se llame el constructor de la clase).  En mi caso concreto se puede hacer de la siguiente forma:

RaceGame g = new RaceGame();

Todo el código necesario para correr tu clase debe estar en el constructor: por esta razón no escribo más código en la función main().  Si tienes dudas con constructores, no dudes en ver este post de mi blog donde lo explico.

Después para dibujar objetos en mi JFrame, es necesario incluir el método paint() en la clase.  Es aquí donde se establecen colores y dibujan figuras.  Esta función la ejecuta el sistema operativo cada vez que necesita redibujar la ventana.

Aquí les dejo un video que explica el programa hasta esta etapa:


Y si quieren el código, lo pueden descargar de aquí.

La imagen de la pisa de carreras que se está dibujando la tomé de la página de Bit Rebels.

La idea de este proyecto fue tomado de este libro:



¡Hasta la próxima!

El Tony y sus ondas...

Related Posts Plugin for WordPress, Blogger...