Imprimir

Movimiento de la bola

Ya hemos visto en el artículo anterior que los objetos de la clase Circle disponen de los métodos setCenterX y setCenterY para poder establecer la posición del círculo en el lugar que se desee de la ventana.

El objetivo del juego Pong es que la bola se vaya moviendo linealmente por la pantalla y que rebote en los lados o en la pala del jugador. En la versión que se explica en este tutorial, se va a desarrollar el juego sólo para 1 jugador, por lo que la bola deberá rebotar en 3 de los lados (arriba, abajo e izquierda) y en la pala del jugador (que se situará en la parte derecha). Si la bola alcanza el extremo derecho de la pantalla sin que haya rebotado en la pala, el jugador perderá esa partida.

En principio vamos a intentar que la bola simplemente se mueva en una dirección, para luego hacer que vaya rebotando en los 4 lados de la pantalla. Será posteriormente cuando se introduzca el funcionamiento de la pala, pero de momento, con que la bola rebote por toda la pantalla tenemos suficiente.

Clase AnimationTimer

Para realizar el movimiento sin la intervención del usuario necesitamos algún mecanismo que permita la repetición de un bloque de código cada cierto tiempo, de manera bastante rápida para que el movimiento de la bola sea lo suficientemente rápido. Cada vez que se repita esa bloque de código se deberá ir cambiando la posición de la bola, y así se conseguirá el movimiento.

Para este tipo de situaciones, JavaFX ofrece la clase Timeline. Se podría pensar también en hacerlo con la utilización de los típicos bucles que suelen incluir los lenguajes de programación, pero conviene asegurar que las repeticiones del código se realicen manteniendo un ritmo constante independientemente del procesador del equipo en el que se ejecute la aplicación. Un bucle se repetirá de manera más rápida en un equipo más potente que en otro más lento, por lo que la velocidad del juego no sería la misma en un equipo y en otro. En cambio, la clase Timeline ofrece la posibilidad de poder repetir código procurando mantener un ritmo continuo, independientemente de la capacidad de procesamiento del equipo en el que se ejecute la aplicación.

Como suele ser habitual en el uso de clases en Java, para poder utilizar la funcionalidad que ofrece, hay que crear un objeto de dicha clase. En el caso de la clase Timeline, la creación de los objetos no es tan simple como en los casos más habituales, como los que ya conoces, en los que se empleaba algo como:

Timeline animationBall = new Timeline();

En el caso de esta clase nos permite crear dentro de la creación del objeto todo el código que deseamos que se repita mientras dure la animación. Escribe el siguiente código dentro del método start que has estado desarrollando hasta ahora, para que se ejecute al iniciar la aplicación, como el resto del código que ya tienes dentro de ese método start.

Screenshot 2021 01 12 at 21.31.04 ef76d

Observa que dentro de la creación del objeto Timeline se está creando un objeto de la clase KeyFrame, indicando como parámetro una duración en segundos. En concreto se está indicando 0.017 segundos. Podrías cambiar el valor de esa duración, pero el indicado corresponde a que se ejecute 60 veces por segundo todo el código que contenga a continuación, que es una frecuencia adecuada para la animación que deseamos. 

Pero ten en cuenta que el código que escribas dentro de ese bloque de código, no se ejecutará hasta que indiques expresamente que se inicie la animación. Esta acción debes hacerla llamando al método play() que también tiene la clase Timeline. Es decir, en el momento en que desees que se inicie la animación debes ejecutar:

Screenshot 2021 01 12 at 21.31.38 e3f7a

En el caso de la aplicación que estás desarrollando se desea que se inicie la animación en cuanto se inicia la aplicación, por lo que también debes incluir la sentencia anterior dentro del método start de la Application que estás desarrollando, tras haber creado el objeto animationBall. Piensa que si quisieras que se iniciara la animación al pulsar, por ejemplo, un botón, se debería incluir esa sentencia en el código de dicho botón y no al iniciar la aplicación.

Además de eso deberás indicar el número de veces que deseas que se ejecute la animación. Para el caso que nos ocupa nos interesa que se ejecute de manera indefinida, para que el juego continúe constantemente, por lo que también deberás incluir una llamada al método setCycleCount() con el objeto de la clase Timeline que hemos creado, indicando por parámetro el valor Timeline.INDEFINITE. Por tanto, todo este bloque de código puede ir quedando así:

Screenshot 2021 01 12 at 21.36.48 03b22

Dentro del código de la animación habrá que incluir la llamada a los métodos setCenterX y setCenterY de la bola, indicando como parámetro de la posición un valor que deberá ir incrementándose sucesivamente. Para entenderlo mejor vamos a centrarnos inicialmente sólo en la posición del eje X.

Hasta ahora habíamos situado la bola en la posición 10 del eje X. Cada vez que se ejecute el código de la animación (recuerda que será unas 60 veces por segundo) se debería incrementar esa posición, por ejemplo, en 1 unidad. Es decir, inicialmente estára en la posición 10, en la siguiente vuelta de la animación deberá colocarse en la posición 11, luego en la 12, la 13, y así sucesivamente.

Por tanto, necesitamos una variable que vaya almacenando esa posición y cuyo contenido vaya cambiando, incrementándose en 1 unidad cada vez que se ejecute una iteración de la animación.

Para declarar una variable de tipo numérico entero dispones de los siguientes tipos básicos en Java: byte, short, int y long. Cada tipo consume una determinada cantidad de memoria, y tiene diferentes valores máximo y mínimo para almacenar cantidades numéricas, como puedes ver en el siguiente esquema, donde aparecen todos los tipos de datos básicos de Java.

tipos de dato e7ce4

Imagen obtenida de www.arkaitzgarro.com

Vamos a utilizar el tipo de dato int para esa variable que podemos llamar ballCenterX, ya que va a almacenar la posición en el eje X del centro de la bola.

Para declarar una variable en Java debes indicar el tipo de dato que se le quiere asignar, seguido del nombre deseado para la variable.

Por tanto, podemos declarar la variable con:

int ballCenterX;

Pero si no se asigna ningún valor a una variable numérica, ésta tomará el valor 0, y en nuestro caso queremos que inicialmente valga 10, para que la bola aparezca inicialmente en esa posición.

Para inicializar una variable en su declaración, utiliza el signo = seguido del valor que se le desea asignar, teniendo en cuenta que el valor debe ser compatible con el tipo de dato asignado a la variable.

Así que modificamos la declaración anterior para inicializar la variable así:

int ballCenterX = 10;

Inicialmente podemos pensar en colocar esta sentencia dentro del código de la animación, así que vamos a colocarla así:

Screenshot 2021 01 12 at 21.38.06 2fdc8 

Nuestro deseo es que esa variable se vaya incrementando sucesivamente, por lo que necesitamos conocer un operador que permita incrementar una variable numérica. Esto podemos hacerlo en Java de cualquiera de estas maneras para el caso de incrementos de 1 en 1:

ballCenterX = ballCenterX + 1;
ballCenterX += 1;
ballCenterX++;

El operador ++ sólo se puede utilizar cuando se quiere incrementar la variable de 1 en 1. Para incrementos mayores habría que usar el operador +=.

La manera más elegante es la última, por lo que añade esa línea. Y antes de usarlo en el movimiento de la bola vamos a ver siu realmente este código va a hacer lo que deseamos. Para ello vamos a mostrar en la salida estándar (la ventana Output en el caso de NetBeans) el valor que va tomando la variable ballCenterX.

Puedes mostrar en la salida estándar cualquier valor literal, variable o resultado de una expresión con la sentencia System.out.print() o System.out.println(). Ésta última añade un salto de línea al final de lo que muestre.

Screenshot 2021 01 12 at 21.39.22 a565b

Ejecuta el código anterior y observa que en la salida estándar aparece lo siguiente:

Screen Shot 2018 01 11 at 22.03.35 51f57

Queríamos que el valor de la variable se fuera incrementando sucesivamente, pero no lo está haciendo. ¿Qué está ocurriendo? Siempre que tengas problemas con el código que escribas procura analizar mentalmente qué hace cada sentencia que has escrito.

Cada vez que se repite el código de la animación se comienza asignando el valor 10 a la variable. A continuación se incrementa en 1, por lo que pasa a tener almacenado el valor 11. Luego se muestra en pantalla, por lo que aparece un 11. En la siguiente iteración de la animación se repite todo eso, por lo que vuelve a inicializarse la variable en 10.

Cuando tengas un problema de este tipo, recuerda que debes declarar e inicializar la variable de manera que sólo se haga al iniciarse el código. Esto puedes hacerlo declarando la variable fuera de cualquier método de la clase que estés desarrollando, es decir, justo después de la declaración de la clase (la que contiene la palabra class).

Screenshot 2021 01 12 at 21.41.26 22c7d 

Observa que ahora la variable ha pasado a tener color verde en NetBeans, por tratarse ahora de una variable o propiedad de la clase que estás desarrollando, en lugar de una variable local como era antes dentro de las llaves en las que estaba encerrada la variable.

Si ejecutas de nuevo la aplicación, verás que ahora sí, el valor de la variable se va incrementando y a una velocidad bastante rápida.

Screen Shot 2018 01 11 at 22.18.32 1a012 

Pues ya estamos seguros de poder utilizar esa variable para posicionar la bola en el eje X, así que la utilizamos como parámetro del método setCenterX sobre el objeto circleBall, y ya puedes eliminar  la sentencia que muestra la variable, y la colocaremos antes de hacer el incremento de la variable para que se inicie la animación partiendo desde la posición inicial (10).

Screenshot 2021 01 12 at 21.44.38 b957e

También conviene utilizar la variable ballCenterX para establecer la posición inicial de la bola en el momento de crear el objeto, cambiando el valor literal 10 por la variable, que tendrá el mismo valor en el inicio de la aplicación:

Screen Shot 2018 01 12 at 08.00.37 a13e2

Ejecuta la aplicación ahora, que ya debe moverse la bola!!!

Cambio de la velocidad

Quizá resulte algo lento el movimiento a esa velocidad para el juego que estamos desarrollando, o bien podriamos pensar que en un futuro nos podría gustar desarrollar la aplicación de manera que la bola vaya incrementando su velocidad según vaya pasando el tiempo de la partida.

Para ello tenemos que plantearnos que el incremento de la variable no sea de 1 en 1, sino que se incremente de 2 en 2, 3 en 3, o el valor que se vea adecuado. Si, por ejemplo, se establece un incremento de 3 en 3, la variable ballCenterX seguirá tomando inicialmente el valor 10, pero en cada vuelta de la animación se incrementará en 3, pasando a valer 13, 16, 19, etc. Así conseguiremos que la bola se mueva más rápidamente.

Así que ahora no nos valdrá el operador ++ y deberemos usar el operador += indicando el valor a incrementar la variable:

Screenshot 2021 01 12 at 21.45.56 65a99

Comprueba que la bola se mueve ahora más rápido.

Pero en vista de que nos interese cambiar la velocidad de la bola en algún momento, vamos a almacenar el valor que se va a utilizar para el incremento en una nueva variable, que también situaremos en la parte superior para poder tener acceso a ella desde cualquier lugar del código y evitar el problema que tuvimos con la otra variable al mantener siempre el mismo valor si se declarada dentro del Timeline.  

Screenshot 2021 01 12 at 21.47.21 79c59

Screenshot 2021 01 12 at 21.48.10 595fc

Rebote en los laterales

Al ejecutar la aplicación podrás comprobar que la bola va de izquierda a derecha y se pierde en el infinito. Esto sucede porque la variable ballCenterX se va incrementando siempre de la misma manera sin límite.

Vamos a intentar que al llegar al borde derecho rebote para volver hacia la izquierda. Para ello habrá que tener en cuenta que la variable ballCenterX debe pasar de irse incrementando de 3 a 3 para irse decrementando en esa misma cantidad. O lo que es lo mismo, para ir hacia la derecha se suma 3 y para ir a la izquierda se puede sumar (para mantener el mismo código) -3.

Así que si conseguimos detectar que la bola ha llegado al borde de la pantalla, debemos cambiar en ese momento el incremento de 3 a -3, y a partir de ese momento en cada vuelta de la animación se restará 3 (sumará -3 que es lo mismo) a la posición de la bola.

Esto lo puedes conseguir utilizando una sentencia condicional, es decir, hay que indica que SI la bola ha alcanzado el borde derecho de la pantalla (posición 600, recuerda el tamaño que se ha establecido para la pantalla), la variable de incremento que marca la velocidad de la bola debe pasar a valer -3.

La sentencia if de Java permite ejecutar un bloque de sentencias si se cumple una determinada condición.
     if(expresión condicional) {
          sentencias a ejecutar si se cumple la condición
     }

Por tanto, la condición de la sentencia if debe comprobar si la variable ballCenterX es mayor o igual que 600.

Para establecer expresiones condicionales en Java se debe utilizar alguno de los siguientes operadores relacionales:
     > Mayor que
     < Menor que
     >= Mayor o igual que (no se puede poner al revés =>)
     <= Menor o igual que (no se puede poner al revés =<)
     == Igual (OJO, no confundir con = que se usa para asignar valores a variables)
     != Distinto

 

Así que el código debe quedar como:

Screen Shot 2018 01 11 at 22.53.39 5569a

Comprueba que ahora la bola no se sale por el borde derecho ejecutando la aplicación, pero si dejas que continúe hacia la izquierda observarás que la bola se sale por el borde izquierdo.

Hay que hacer algo similar si la posición de la bola llega a alcanzar el valor 0. En ese caso, la velocidad de la bola debe volver a ser positiva:

Screen Shot 2018 01 11 at 22.56.39 9be86 c74bc

Comprueba que la bola ya no desaparece por ninguno de los laterales.

Rebote en todos los bordes

Para que el movimiento de la bola no sólo sea horizontal, hay que hacer prácticamente las mismas operaciones que se han hecho para la posición en el eje X, pero ahora también para el eje Y.

Es decir, declarar una variable como propiedad de la clase para la posición de la bola en el eje Y, y otra para la velocidad en ese eje:

Screen Shot 2018 01 12 at 08.25.03 337c3

En conveniente usar la variable de la posición Y en el constructor de la bola, en lugar del valor literal que se usaba, para asegurar que se inicie en la posición marcada por el valor inicial de la variable (30):

Screen Shot 2018 01 12 at 08.26.25 572e5

Establecer, dentro de la animación, la posición de la bola según el valor de esa misma variable, y controlar la velocidad en el eje Y teniendo en cuenta los límites superior e inferior de la ventana:

Screen Shot 2018 01 12 at 08.27.34 07dbd