Creación de la pala (stick)

La pala o stick que va a controlar el jugador la vas a crear usando un rectángulo, que es otra de las formas geométricas que permite utilizar JavaFX.

Los rectángulos se pueden crear con la clase Rectangle. Según la API, se dispone de los siguientes métodos constructores:

Captura b5368

Podemos utilizar la tercera opción, donde se van a indicar las posiciones x e y, el ancho y el alto. También habrá que cambiar su color, pero no existe ningún método constructor que nos ofrezca todo, por lo que habrá que usar después el método que permite cambiar su color.

La posición del rectángulo en el eje Y será variable, ya que será el usuario el que pueda ir cambiando esa posición usando el teclado. Por tanto, hay que crear una nueva variable para esa posición, y la utilizaremos directamente en el método constructor para que se posicione directamente en el valor inicial que se le asigne a la variable.

Declara la siguiente variable como una propiedad de la clase (fuera de los métodos, por debajo de la declaración de la clase), inicializándola en la mitad de la altura de la ventana (aunque así no quedará exactamente centrado, ya que la posición del rectángulo está indicada por su esquina superior izquierda):

Captura 145f9

Ahora crea un objeto de la clase Rectangle dentro del método start  de la aplicación, asignándole, por ejemplo, 500 (cerca del borde derecho de la ventana) a la posición del eje X, la variable anterior al eje Y, 7 puntos de ancho y 50 de alto. Cambia su color de fondo y añádelo a la lista de hijos del layout principal (root):

Captura 99250

Recuerda que debes reparar las importaciones para que se reconozca la clase Rectangle. En este caso se vuelve a dar la situación de que existen varias clases llamadas Rectangle en las librerías de Java, por lo que debes asegurarte de excoger aquella que pertenece al paquete javafx:

Captura afdbe

Centrar correctamente la pala

Ejecuta la aplicación y comprueba que ya aparece la pala, aunque si te fijas bien, no aparece bien centrada verticalmente aunque se haya indicado que coloque el rectángulo en la mitad del tamaño vertical de la ventana. Esto es debido a que la posición del rectángulo se toma en referencia a su esquina superior izquierda, por lo que será la parte superior del rectángulo la que esté en el centro de la ventana. Podemos arreglarlo restando la mitad de la altura del rectángulo (50/2) a la posición que ya teníamos del eje Y: 400/2 - 50/2 o lo que es lo mismo: (400-50)/2.

Captura 99b5e

Ahora ya sí debe aparecer la pala centrada:

Captura 8db13

El lenguaje Java ofrece los siguientes operadores aritméticos:
     + Suma
     - Resta
     * Multiplicación
     / División (resultado con decimales si alguno de los operandos es un número real float o double)
     % Resto
Las expresiones aritméticas siguen el orden de precedencia matemático, por lo que las sumas y restas se harán después del resto de operaciones. Puedes usar paréntesis para cambiar el orden de las operaciones.

Observa que según estas reglas de las operaciones aritméticas, no es lo mismo indicar (400-50)/2 que lo mismo sin paréntesis 400-50/2, ya que se haría primero la división realizando 400-25=375 en vez de 350/2=175. 

Uso de constantes

Poco a poco el código va creciendo y cada vez se están utilizando más valores literales en él y se va complicando su lectura. Ten en cuenta que cuando pase un tiempo y tengas que volver a retomar el código que hayas estado haciendo no recordarás a qué hace referencia el valor 400 o el 50, por ejemplo. Y lo mismo le ocurrirá a otra persona que vaya a retorcar tu código. Además, si por algún motivo fuera necesario o nos apeteciera cambiar el tamaño vertical de la ventana, no se nos puede olvidar que también hay que cambiar la posición de la pala. Por todo ello, es conveniente evitar el uso de valores literales significativos en las sentencias y usar en su lugar variable o contantes cuyo nombre haga más legible el código y más fácil de modificar.

Las constantes en Java son similares a las variables, pero se utilizan cuando se sabe que su valor no va a ser modificado en ningún momento durante la ejecución de la aplicación. Se declaran usando la palabra final antes del tipo de dato y se acostumbra a indicar su nombre completo en mayúsculas usando guiones bajos para separar las palabras.

Dos contantes claras para esta aplicación pueden ser las medidas de la ventana (600 x 400) que podemos declarar así de manera global, como propiedades de la clase, para que puedan ser utilizadas en cualquier parte del código:

Captura e477f

Ahora, cada vez que se haga referencia a esas medidas, se debe usar el nombre de las constantes en lugar del valor literal. Por ejemplo, la creación del objeto de la clase Scene debe quedar:

Captura 614d9

En el control de los rebotes de la bola también se hacía referencia a las medidas de la ventana, por lo que también ahí debes usar estas constantes, tanto en la posición X como en la Y de la bola:

Captura 83884

También podemos crear unas constantes para las medidas de la pala, y usarlas en el cálculo de su posición inicial :

Captura cdd63

Y modifica la creación del rectángulo para usar también esas constantes. Observa que podemos posicionar el rectángulo de manera relativa al ancho que tenga la ventana. En este código se coloca al 90% de lo que mida el ancho de la ventana. Así, aunque se cambie el tamaño de la ventana, siempre quedarán todos los elementos de manera que sea jugable.

Captura b4e7c

Prueba a modificar (en las constantes) momentáneamente las medidas de la ventana, por ejemplo a 300 x 200 y comprueba que la pala se sigue colocando centrada verticalemente cerca del borde derecho y que la bola rebota correctamente en todos los bordes de la ventana.

Captura 717c8

Movimiento de la pala

Para habilitar el movimiento de la pala vamos a proceder de manera similar al modo en que se ha programado el movimiento de la bola, es decir, dentro del AnimationTimer se va a asignar su posición, la cual se irá incrementando o decrementando según se pulse la tecla abajo o arriba.

Detección de pulsación de teclas

Para detectar si el usuario ha pulsado una tecla podemos utilizar el método setOnKeyPressed con el objeto de la clase Scene de la aplicación. Este método también tiene un modo de uso menos directo que los habituales, ya que hay que asignarle por parámetro un objeto de la clase EventHandler en el que hay que sobreescribir un método llamado handle, de manera similar a como se había hecho con el AnimationTimer. La estructura básica es como la siguiente:

scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
    @Override
    public void handle(KeyEvent event) {

    }
});

Nuestro objetivo es que la pala se vaya moviendo mientras esté pulsada la tecla arriba o abajo, y que se detenga cuando se deje de pulsar. Así que necesitamos detectar también cuándo se ha dejado de pulsar una tecla, por lo que habrá que usar también el método setOnKeyReleased que programaremos de manera similar:

scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
    @Override
    public void handle(KeyEvent event) {

    }
});

Ambos bloques de código se pueden incluir en el interior del método start de la aplicación, de manera que se tendrá en cuenta el código que incluyan desde que se inicie la aplicación. Recuerda que tendrás que reparar las importaciones, seleccionando los paquetes de JavaFX si se pregunta.

Al incluir ambos bloques de código puedes observar que NetBeans subraya en amarilo EventHandler<KeyEvent> y si colocas el cursor del ratón sobre el aviso verás que muestra un mensaje notificando que se puede convertir en una expresión lambda.

Captura a4dcf

Haz clic en el icono amarillo de aviso del margen izquierdo y selecciona la opción Use lambda expression que aparece: 

Captura 7effa 

Verás que si lo haces en ambos bloques de código se quedan de manera más abreviada de la siguiente manera:

Captura 59848

Las expresiones lambda han sido incluidas en Java a partir de la versión 8 para simplificar algunas estructuras de código.

En cualquiera de los casos, observa que se dispone del parámetro event, de la clase KeyEvent, que te va a permitir conocer qué tecla se ha pulsado o se ha soltado. Usando la llamada al método event.getCode() puedes obtener el código de la tecla que ha usado el usuario. Estos códigos hacen referencia a una serie de propiedades constantes declaradas en la clase KeyCode, por lo que su uso se simplifica al utilizarse el nombre de las teclas en vez de un código numérico asociado a cada tecla.

Así, si por ejemplo quisieras conocer si se ha pulsado la tecla Arriba, se podría preguntar algo como:

Captura 1b27f

Pero no sólo hay que comprobar si se ha pulsado esa tecla, sino también la tecla Abajo, y según la aplicación que se desarrolle, puede que se necesiten comprobar unas cuantas teclas, por lo que en lugar de utilizar la sentencia if es más útil usar la sentencia de selección switch de Java (esta sentencia también es usada por la mayoría de los lenguajes de programación).

La sentencia switch permite la ejecución de un bloque de sentencias determinado en función del valor resultante de la evaluación de una expresión numérica. Se emplea una estructura similar a la siguiente:
    switch(expresión) {
        case valor1:
            // Sentencias a ejecutar si el resultado de la expresión es valor1
            break;
        case valor2:
            // Sentencias a ejecutar si el resultado de la expresión es valor2
            break;
        default:
            // Sentencias a ejecutar si el resultado de la expresión
            // no es ninguno de los valores anteriores

    }
El bloque default es opcional, y si no se indicara la sentencia break en alguno de los casos, se ejecutaría el bloque de sentencias del caso correspondiente y el del bloque siguiente.

De esta manera se puede emplear un código como el siguiente para controlar la pulsación de las teclas, ya que realmente los códigos de las teclas son valores numéricos asociados a cada constante:

Captura a8020

A partir de aquí, el resto de las operaciones a realizar serán similares a las que se han empleado para controlar el movimiento de la bola.

Crear una variable como propiedad de esta clase para determinar el incremento o decremento de la posición Y de la pala. Inicialmente la pala aparecerá parada, por lo que se inicializa al valor 0:

Captura 505f8

Si está pulsada la tecla Arriba, esa variable tomará un valor negativo, por ejemplo -6, para que cuando se sume a la posición actual de la pala ésta suba hacia arriba. Si se pulsa Abajo se asigna un valor positivo, y si se ha soltado cualquiera de ellas se debe detener asignando el valor 0:

Captura cc399

Dentro del método handle del AnimationTimer se debe actualizar la variable que marca la posición de la pala y asignar dicha posición al rectángulo que la representa:

Captura 2ea01

Prueba a ejecutar la aplicación y comprueba que la pala se desplaza arriba y abajo al pulsar las teclas indicadas.

Control de los límites de la pala

Durante la prueba del movimiento de la pala podrás observar que la pala desaparece de la vista al superar el borde inferior o superior de la ventana. Sería controlar que la pala no sobrepase esos límites para que el usuario siempre tenga a la vista la pala.

Para determinar si la pala ha sobrepasado el borde superior se puede preguntar si la variable de la posición vertical de la pala ha obtenido un valor menor que 0 después de haber sido actualizada al nuevo valor. Eso significaría que se va a colocar el borde superior izquierdo de la pala (el que marca la posición del rectángulo) fuera de la ventana. En ese caso podemos reasignar el valor de su posición para igualarla a 0 y que así se mantenga justo en el borde superior.

Captura 21890

Ejecuta la aplicación y comprueba que la pala no sobrepasa el borde superior.

Si no fuera esa la situación, habría que comprobar si se ha pasado la pala por el borde inferior.

La sentencia if se puede complementar con la palabra else para ejecutar un bloque de sentencias en el caso de que no se cumpla la expresión condicional indicada junto al if, siguiendo una estructura como la siguiente:
    if(expresiónCondicional) {
        // Sentencias a ejecutar si se cumple la condición
    } else {
        // Sentencias a ejecutar si NO se cumple la condición
    } 

El control del borde inferior se puede hacer en la parte del else del if anterior, ya que si la pala ha sobrepasado el borde superior es innecesario controlar si se ha pasado por el borde inferior y se obtiene así un código más eficiente:

Por tanto, en caso de que NO se haya superado el borde superior, se preguntará si se ha superado el borde inferior. Si eso ocurriera, se mantendrá el rectángulo en esa posición. Ruerda que habíamos creado una constante para determinar la altura de la ventana, así que podríamos escribir:

Captura aafd4

Si ejecutas ahora la aplicación observará que la pala sigue desapareciendo por la parte inferior, aunque se queda justo después del borde sin ir más allá. Esto ocurre porque la posición del rectángulo está determinada por la posición de su esquina superior izquierda. Al preguntar si la posición del rectángulo es superior al tamaño de la ventana, estamos preguntando si la parte superior del rectángulo está más abajo del borde de la ventana.

PongFX 5f988

Para arreglar este detalle podemos preguntar si el borde superior del rectángulo (la posición del rectángulo) ha sobrepasado el límite de la ventana menos la medida de la altura del rectángulo. Si esto ocurriera, la posición del rectángulo se debe mantener en ese límite:

Captura 8c83d

Comentarios en el código fuente

Seguramente habrás observado que en los trozos de código fuente anteriores se han introducido líneas que no corresponden a sentencias del lenguaje Java, y que indican de manera textual el propósito de las líneas de código. Se trata de comentarios, que se suelen incluir en los códigos fuente de las aplicaciones para especificar en un lenguaje natural la funcionalidad de un bloque de código.

Los comentarios de una línea se indican en Java comenzando por // y si se quiere introducir un comentario de más de una línea se debe comenzar por /* y finalizar con */

Añade comentarios de una línea en los apartados del código que consideres que deben quedar detallados con una explicación y un comentario de varias líneas, al principio del archivo del código fuente que estás desarrollando, para explicar el objetivo de esta aplicación así como la licencia que desees asignar a tu código fuente. En el siguiente ejemplo se indica la licencia GPL (software libre) para este código fuente. Puedes copiar el texto correspondiente a cada tipo de licencia desde los sitios web de cada una de ellas.

Captura 0ded2