Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

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 nómbralo ScoreLabel

|image1|

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

image2

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 Theme Overrides, expande Colors y activa Font Color para cambiar el tinte del texto a negro (el cual contrasta bien con la escena 3D blanca)

|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).

Este campo espera un archivo de fuente como los que tienes en tu computadora. Los archivos de fuente comunes son TrueType Font (TTF) y OpenType Font (OTF).

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

El texto es un poco pequeño. Cambia el Default Font Size a 22 pixeles para incrementar el tamaño del texto.

image7

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 a ScoreLabel 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.

image8

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_mob_timer_timeout(), agrega la siguiente línea:

func _on_mob_timer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

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 callback _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 símbolo %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

Puedes aprender más sobre formateo de cadenas aquí Cadenas de formato en GDScript. En C#, considera usar interpolación de cadenas con "$".

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

image9

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.tscn, 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ú Anchor Preset en la barra de herramientas.

image10

Ábrelo y aplica el comando Full Rect.

image11

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

image12

Esto se debe a que los nodos de 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 Anchor Presets -> Completo 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/transparencia.

image13

Ahora, agrega un Label como hijo de Retry y en el Text escribe "Press Enter to retry." Para moverlo y anclarlo en el centro de la pantalla, aplica Anchor Preset -> Center a este.

image14

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 el callback incorporado _unhandled_input() que se dispara con cualquier entrada.

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 o usando el icono + que está junto a la escena abierta.

image15

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

image16

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.

image17

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.

image18

MusicPlayer.tscn ahora carga con cualquier escena que abras o ejecutes. Así que si ejecutas el juego ahora, la música se reproducirá automáticamente en cualquier escena.

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.

image19

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.

image20

En la parte superior se encuentran el nodo autocargado MusicPlayer y un nodo root, que es el viewport 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 var mob_scene: PackedScene

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


func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

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

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()