Puntaje y repetición

En esta parte, agregaremos la puntuación, la reproducción de música y la capacidad de reiniciar el juego.

Tenemos que llevar un registro de la puntuación actual en una variable y mostrarla en pantalla utilizando una interfaz mínima. Utilizaremos una etiqueta de texto para hacerlo.

En la escena principal, agrega un nuevo nodo Control como hijo de Main y nómbralo UserInterface. Serás llevado automáticamente a la pantalla 2D, donde podrás editar tu Interfaz de Usuario (UI).

Agrega un nodo Label y renómbralo como ScoreLabel.

image0

En el Inspector, establece el Texto del Label como un marcador de posición como "Puntuación: 0".

|image1|

Además, el texto es blanco por defecto, al igual que el fondo de nuestro juego. Necesitamos cambiar su color para poder verlo durante la ejecución.

Desplázate hacia abajo hasta Sobrescritos de Tema, y expande Colores y haz clic en el cuadro negro junto a Color de Fuente para cambiar el color del texto.

image2

Elige un tono oscuro para que contraste bien con la escena en 3D.

|image3|

Finalmente, haz clic y arrastra el texto en el viewport para moverlo lejos de la esquina superior izquierda.

image4

El nodo UserInterface nos permite agrupar nuestra UI en una rama del árbol de escenas y utilizar un recurso de tema que se propagará a todos sus hijos. Lo utilizaremos para establecer la fuente de nuestro juego.

Creando un tema de interfaz de usuario

Una vez más, selecciona el nodo UserInterface. En el Inspector, crea un nuevo recurso de tema en Theme -> Theme.

image5

Haz clic en él para abrir el editor de temas en el panel inferior. Te brinda una vista previa de cómo se verán todos los widgets de la UI incorporados con tu recurso de tema.

image6

Por defecto, un tema solo tiene una propiedad, la Default Font.

Ver también

Puedes agregar más propiedades al recurso de tema para diseñar interfaces de usuario más complejas, pero eso está fuera del alcance de esta serie. Para obtener más información sobre cómo crear y editar temas, consulta Introducción al skinning de la interfaz gráfica de usuario (GUI).

Haz clic en la propiedad Default Font y crea una nueva DynamicFont.

image7

Expande la DynamicFont haciendo clic en ella y expande su sección Font. Allí, verás un campo vacío llamado Font Data.

image8

Este campo espera un archivo de fuente como los que tienes en tu computadora. DynamicFont admite los siguientes formatos:

  • TrueType (.ttf)

  • OpenType (.otf)

  • Formato de Fuente Web Abierta 1 (.woff)

  • Formato de Fuente Web Abierta 2 (.woff2, desde Godot 3.5)

En el panel Sistema de Archivos, expande el directorio fonts y arrastra el archivo Montserrat-Medium.ttf que incluimos en el proyecto hacia Font Data. El texto volverá a aparecer en la vista previa del tema.

En el panel Sistema de Archivos, expande el directorio fonts y arrastra el archivo Montserrat-Medium.ttf que incluimos en el proyecto hacia Datos de Fuente. El texto volverá a aparecer en la vista previa del tema.

image9

Manteniendo el rastro del puntaje

A continuación, trabajemos en la puntuación. Adjunta un nuevo script a ScoreLabel y define la variable score.

extends Label

var score = 0

La puntuación debería aumentar en 1 cada vez que aplastemos a un monstruo. Podemos utilizar su señal squashed para saber cuándo ocurre eso. Sin embargo, como instanciamos los monstruos desde el código, no podemos hacer la conexión en el editor.

En cambio, tenemos que hacer la conexión desde el código cada vez que generamos un monstruo.

Abre el script Main.gd. Si aún está abierto, puedes hacer clic en su nombre en la columna izquierda del editor de scripts.

image10

Alternativamente, puedes hacer doble clic en el archivo Main.gd en el panel Sistema de Archivos.

En la parte inferior de la función "_on_MobTimer_timeout()", agrega la siguiente línea.

func _on_MobTimer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")

Esta línea significa que cuando el enemigo emite la señal squashed, el nodo ScoreLabel la recibirá y llamará a la función _on_Mob_squashed().

Regresa al script ScoreLabel.gd para definir la función de devolución de llamada _on_Mob_squashed().

Allí, incrementamos la puntuación y actualizamos el texto mostrado.

func _on_Mob_squashed():
    score += 1
    text = "Score: %s" % score

La segunda línea utiliza el valor de la variable score para reemplazar el marcador de posición %s. Al utilizar esta función, Godot convierte automáticamente los valores a texto, lo cual es conveniente para mostrar texto en etiquetas o utilizando la función print().

Ver también

Usted puede aprender mas acerca del formato de cadenas aqui: Cadenas de formato en GDScript.

Ahora puedes jugar el juego y aplastar algunos enemigos para ver cómo aumenta la puntuación.

image11

Nota

En un juego complejo, es posible que desees separar por completo la interfaz de usuario del mundo del juego. En ese caso, no llevarías un seguimiento de la puntuación en la etiqueta. En su lugar, podrías almacenarla en un objeto separado y dedicado. Sin embargo, al prototipar o cuando tu proyecto es simple, está bien mantener tu código simple. La programación siempre es un acto de equilibrio.

Reintentando el juego

Ahora agregaremos la capacidad de jugar nuevamente después de morir. Cuando el jugador muera, mostraremos un mensaje en la pantalla y esperaremos una entrada.

Regresa a la escena Main, selecciona el nodo UserInterface, agrega un nodo ColorRect como hijo de este y nómbralo Retry. Este nodo llenará un rectángulo con un color uniforme y servirá como una superposición para oscurecer la pantalla.

Para hacer que abarque toda la ventana de visualización, puedes utilizar el menú Layout en la barra de herramientas.

image12

Ábrelo y aplica el comando Full Rect.

image13

No sucede nada. Bueno, casi nada: solo los cuatro pines verdes se mueven a las esquinas del cuadro de selección.

image14

Esto se debe a que los nodos de la UI (todos los que tienen un ícono verde) funcionan con anclajes y márgenes relativos al cuadro delimitador de su padre. Aquí, el nodo UserInterface tiene un tamaño pequeño y el nodo Retry está limitado por él.

Selecciona el nodo UserInterface y también aplica Layout -> Full Rect en él. Ahora, el nodo Retry debería abarcar toda la ventana gráfica.

Cambiemos su color para que oscurezca el área del juego. Selecciona Retry y en el Inspector, establece su Color en algo oscuro y transparente. Para hacerlo, en el selector de color, arrastra el deslizador A hacia la izquierda. Este controla el canal alfa del color, es decir, su opacidad.

image15

A continuación, agrega un nodo Label como hijo de Retry y dale el Texto "Presiona Enter para intentarlo de nuevo"

image16

Para moverlo y anclarlo en el centro de la pantalla, aplica Layout -> Center en él.

image17

Codificando la opcion retry

Ahora podemos dirigirnos al código para mostrar y ocultar el nodo Retry cuando el jugador muere y vuelve a jugar.

Abre el script Main.gd. Primero, queremos ocultar la superposición al inicio del juego. Agrega esta línea a la función _ready().

func _ready():
    #...
    $UserInterface/Retry.hide()

Luego, cuando el jugador recibe un golpe, mostramos la superposición.

func _on_Player_hit():
    #...
    $UserInterface/Retry.show()

Finalmente, cuando el nodo Retry es visible, necesitamos escuchar la entrada del jugador y reiniciar el juego si presionan Enter. Para hacer esto, utilizamos la función de devolución de llamada incorporada _unhandled_input().

Si el jugador presiona la acción de entrada predefinida "ui_accept" y Retry es visible, recargamos la escena actual.

func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        # This restarts the current scene.
        get_tree().reload_current_scene()

La función get_tree() nos da acceso al objeto global SceneTree, lo que nos permite recargar y reiniciar la escena actual.

Añadiendo musica

Para agregar música que se reproduzca continuamente en segundo plano, vamos a utilizar otra característica de Godot: los autoloads.

Para reproducir audio, todo lo que necesitas hacer es agregar un nodo AudioStreamPlayer a tu escena y adjuntarle un archivo de audio. Cuando inicias la escena, puede reproducirse automáticamente. Sin embargo, cuando vuelves a cargar la escena, como lo hacemos para jugar nuevamente, los nodos de audio también se reinician y la música vuelve a comenzar desde el principio.

Puedes utilizar la función de autoload para que Godot cargue automáticamente un nodo o una escena al inicio del juego, fuera de la escena actual. También puedes utilizarla para crear objetos de acceso global.

Crea una nueva escena yendo al menú Escena y haciendo clic en Nueva Escena.

image18

Haz clic en el botón Otro Nodo para crear un AudioStreamPlayer y renómbralo como MusicPlayer.

image19

Incluimos una banda sonora en el directorio art/, llamada House In a Forest Loop.ogg. Haz clic y arrástrala hasta la propiedad Stream en el Inspector. Además, activa Autoplay para que la música se reproduzca automáticamente al inicio del juego.

image20

Guarda la escena como MusicPlayer.tscn.

Tenemos que registrarlo como una carga automática. Ve al menú Proyecto -> Configuración del proyecto... y haz clic en la pestaña Carga automática.

En el campo Path, debes ingresar la ruta de tu escena. Haz clic en el ícono de la carpeta para abrir el explorador de archivos y haz doble clic en MusicPlayer.tscn. Luego, haz clic en el botón Add a la derecha para registrar el nodo.

image21

Si ejecutas el juego ahora, la música se reproducirá automáticamente. Incluso cuando pierdas y vuelvas a intentarlo, seguirá reproduciéndose.

Antes de finalizar esta lección, aquí tienes un vistazo rápido de cómo funciona internamente. Cuando ejecutas el juego, tu panel Escena cambia para mostrarte dos pestañas: Remote y Local.

image22

La pestaña Remote te permite visualizar el árbol de nodos de tu juego en ejecución. Allí verás el nodo Main y todo lo que contiene la escena, así como los enemigos instanciados en la parte inferior.

image23

En la parte superior se encuentran los nodos MusicPlayer cargado automáticamente y un nodo root, que es el visor del juego.

Y eso es todo para esta lección. En la próxima parte, agregaremos una animación para que el juego se vea y se sienta mucho mejor.

Aquí está el script completo Main.gd como referencia.

extends Node

export (PackedScene) var mob_scene


func _ready():
    randomize()
    $UserInterface/Retry.hide()


func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        get_tree().reload_current_scene()


func _on_MobTimer_timeout():
    var mob = mob_scene.instance()

    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    mob_spawn_location.unit_offset = randf()

    var player_position = $Player.transform.origin
    mob.initialize(mob_spawn_location.translation, player_position)

    add_child(mob)
    mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")


func _on_Player_hit():
    $MobTimer.stop()
    $UserInterface/Retry.show()