Puntuaciones del juego
Cualquier en la mayoría de los juegos, vamos a mostrar al usuario la puntuación que lleva en la partida, así como la puntuación máxima que ha obtenido desde que se haya iniciado la ejecución de la aplicación (de momento no tendremos los conocimientos necesarios para que la puntuación máxima se quede almacenada entre una ejecución y otra).
Puede quedar con una estructura similar a la siguiente:
Layouts y componentes Text para cada elemento de texto
Puedes apreciar que se van a utilizar 4 elementos de texto:
- La etiqueta "Score" (a la que se hará referencia con la variable textTitleScore)
- La puntuación actual (textScore)
- La etiqueta "Max.Score" (textTitleHighScore)
- La puntuación máxima (textHighScore)
Para mostrar un texto en pantalla con JavaFX se pueden utilizar las clases Text o Label. En este ejemplo se va a usar la clase Text, que exige menos requerimientos al sistema y tiene las suficientes características para las necesidades actuales.
Por tanto, hay que crear un objeto Text para cada uno de los 4 elementos mencionados antes. Cuando escribas el código siguiente, verás que se usa una constante llamada TEXT_SIZE, que deberás crear tú mismo (¡ya debes saber cómo!), de tipo numérico con el valor que desees para el tamaño de la fuente del texto. En los ejemplos que se muestran se asignó un tamaño de 24.
Estos elementos Text se podrían colocar directamente dentro del layout principal (root) de la escena indicando las coordenadas X e Y donde se desearan colocar, usando los métodos setX y setY. Pero esto tiene la desventaja de que si cambiara el tamaño de la ventana, se descolocarían los textos.
Debes procurar que los elementos de pantalla de las aplicaciones que desarrolles se adapten dinámicamente a distintos tamaños de pantalla, por lo que conviene usar contenedores (layout) para organizarlos. Como podrás ver en el código, se usan layouts de tipo HBox que permiten colocar todos los elementos que contengan uno al lado del otro, en horizontal. Y verás que se utiliza un HBox general, y dentro de él otros 2 HBox, uno para la puntuación y otro para la puntuación máxima.
En la siguiente imagen quizá puedes ver mejor cómo se van a organizar estos elementos. El marco rojo corresponde al layout principal para estos textos (paneScores), el verde es el layout para la etiqueta y puntuación actual (paneCurrentScore), y el azul es el layout para la etiqueta y puntuación máxima (paneHighScore).
El código necesario puede ser como el siguiente:
Observa que si se viera en forma de árbol la organización de los contenedores y de los textos, quedaría como esto:
Contador de puntuación
Vamos a determinar que cada vez que la pala le dé a la bola, se contabilizara 1 punto. Necesitamos una variable donde vayamos contabilizando la puntuación que se va alcanzando, incrementándose de 1 en 1 cada vez que la bola colisione con la pala.
La variable habrá que declararla como una propiedad de la clase, justo después de la declaración de la clase (class), ya que si se declara dentro del método handle del AnimationTimer, la variable volverá a tomar el valor 0 en cada iteración de la animación.
Incrementamos (recuerda el operador ++) la variable cuando se detecte la colisión, e intentamos mostrar el nuevo contenido de esta variable en el campo de texto textScore con el que ya habíamos trabajado antes:
Se detecta un error al intentar mostrar el contenido de la variable score en el Text textScore. Vamos a ver por qué. El método setText de la clase Text permite cambiar el texto que muestra un componente de ese tipo. Si vemos su formato de uso en la API podemos observar que el parámetro que se le pase debe ser de tipo String, y estamos intentando pasar un valor de tipo numérico.
Por eso el error avisa de que se están indicando tipos incompatibles: int no se puede convertir a String.
Para convertir un valor numérico en Java a String se puede usar el método estático valueOf de la clase String, indicando como parámetro el valor numérico y el método retornará el String correspondiente. Por ejemplo:
String strNum = String.valueOf(286.38);
De manera similar, si se desea hacer la operación contraria, convertir un String que contenga dígitos numéricos a su valor numérico se puede usar ese mismo método con las clases asociadas a los tipos numéricos: Integer, Double, Float, etc. Por ejemplo:
double num = Double.valueOf("286.38");
Así que vamos a usar el método estático valueOf de la clase String para hacer la conversión, y arreglaremos el problema:
Como siempre, ejecuta la aplicación y comprueba su funcionamiento. Pues quizá observes que no funciona bien del todo, ya que el sistema de colisiones aún detectará que están colisionando aunque la bola ya vuelva de regreso y se contabilizan más puntos de la cuenta en cada colisión. Para arreglarlo podemos reforzar la detección de la colisión, comprobando también que la dirección de la bola sea hacia la derecha. De esa manera sólo se considera una colisión si la bola se está moviendo en dirección a la pala y no cuando está saliendo de la pala.
Si escribimos en nuestra lengua la sentencia que comprueba la colisión debería quedar como: SI (hay colisión) Y (la bola va hacia derecha). Para saber si la bola va en sentido hacia la derecha podemos preguntar si su velocidad X es mayor que 0 (positiva).
Para unir expresiones relacionales de deben usar los operadores lógicos Y o bien O. En java se representan como && y || (las barras de la tecla 1). Si se deben cumplir las 2 condiciones que se establezcan debes usar el operador &&, pero si es suficiente con que se cumpla una de las condiciones debes usar el operador ||.
Como en este caso es necesario que se cumplan las 2 condiciones (que haya colisión Y que la bola vaya hacia la derecha) hay que usar el operador &&:
Ahora debe llevar bien la cuenta de toques de la pala con la bola.
Puntuación más alta
Había prometido que también se guardara la puntuación más alta conseguida, aunque sólo se mantendrá mientras no se cierre la aplicación. Para que se mantuviera guardada la puntuación máxima entre una ejecución y otra habría que guardar dicha puntuación en un archivo al cerrar la aplicación, y volver a recuperar la puntuación guardada en dicho archivo cuando se volviera a abrir la aplicación. Pero el manejo de archivos queda fuera del objetivo de este tutorial, pero si te animas a buscar información sobre la clase Preferences de Java seguro que consigues algo.
De momento vamos a necesitar otra variable, que también debes declarar como una propiedad de la clase de tu aplicación (por los motivos que ya deberías conocer), para almacenar en ella la puntuación máxima hasta el momento.
Consideraremos que el jugador pierde una partida cuando la bola toque el lado derecho de la ventana, es decir, si la pala no ha dado en la bola para que rebote. Por tanto, en ese momento debes comprobar si la puntuación que ha conseguido el jugador es superior a la puntuación máxima que haya hasta ese momento.
La detección de la colisión con el lado derecho ya lo teníamos implementado. Pero hasta ahora hacíamos que rebotara hacia la izquierda cambiando a negativa la velocidad X de la bola. Así que cambia este código:
Por este otro, donde se hace esa comprobación para ver si la puntuación actual es mayor que la más alta, en cuyo caso la puntuación más alta (highScore) será ahora la puntuación conseguida en la partida actual (score). También se actualizará el campo de texto correspondiente a la puntuación más alta:
Pero no sólo hay que hacer eso, ya que si la bola ha tocado el lado derecho hay que empezar una nueva partida, por lo que habrá que reiniciar todas las variables oportunas para recolocar la bola y poner la nueva puntuación actual a 0: