Tutorial de Godot – Parte 18: Agregando una Casa con Interior

En este tutorial agregaremos una casa al juego, en la cual el jugador podrá ingresar para usar la cama para descansar y recuperar sus puntos de salud y maná. Cuando el jugador esté fuera de la casa, se mostrará todo el edificio. Cuando el jugador ingrese a la casa, el techo se ocultará y se mostrará el interior.

Importación de activos

Primero, descargue las imágenes de la casa haciendo clic en el botón a continuación:

Descargar «Sprites de la casa SimpleRPG»house_sprites.zip – 5 KB

Cree una nueva carpeta llamada Casa dentro de la carpeta Entidades y copie las imágenes que acaba de descargar en ella. Asegúrese de que las imágenes se importen con el filtro desactivado.

Creando la escena de la casa

Crea una nueva escena. En el panel Escena , seleccione Escena 2D para crear un nodo raíz de tipo Nodo2D . Cambie el nombre de este nodo a Casa .

Agregue un nodo Sprite a House y cámbiele el nombre Interior . Arrastre el archivo house_interior.png a la propiedad Textura de este nodo. Agregue otro nodo de Sprite a Casa y cámbiele el nombre Techo . Arrastre el archivo house_roof.png a su propiedad Textura y establezca su Índice Z en 2.

Guarde la escena como House.tscn en la carpeta Entitites/House .

Agregar la casa a la escena principal

Regrese a la escena principal y seleccione el nodo Raíz . Arrastra el archivo House.tscn hasta el punto del mapa donde quieras colocar la casa (debe ser una zona libre de obstáculos, así que edita el mapa si no tienes una zona libre lo suficientemente grande).

Si ejecuta el juego ahora, verá que el jugador podrá caminar por la casa. Tenemos que agregar colisionadores para bloquear el movimiento del jugador cuando se mueve contra las paredes.

Adición de colisionadores para paredes y muebles de la casa.

Vuelva a la escena de la casa y haga que el techo sea invisible temporalmente haciendo clic en el icono del ojo junto al nombre del nodo.

Agregue un nodo StaticBody2D a House y cámbiele el nombre NorthWall . Agregue un nodo CollisionShape2D a NorthWall . En el Inspector , establezca su Forma en New RectangleShape2D . Ahora, en el editor, mueva y cambie el tamaño del rectángulo para cubrir la pared norte como en la siguiente imagen:

Repita el mismo procedimiento para las paredes este y oeste.

El muro sur está dividido en dos partes porque ahí está la puerta principal de la casa, por lo que tendrás que crear dos StaticBody2D ( llámalos SouthWallLeft y SouthWallRight ). Para estos dos nodos, usa un rectángulo un poco más alto para evitar que el jugador se superponga por completo con la pared cuando se mueva hacia ella (esto dará una mayor percepción de tridimensionalidad). Obtendrás algo como esto:

Ahora solo tenemos que crear los colisionadores para las paredes internas y para los muebles. Siga el procedimiento anterior para crear todos los nodos StaticBody2D que necesitamos con los relativos CollisionShape2D y RectangleShape2D . Puede llamar a los nodos StaticBody2D como desee, pero asegúrese de llamar a la cama Bed porque la necesitaremos más adelante.

El resultado final será así:

Guarda la escena e intenta iniciar el juego. Ahora el jugador estará bloqueado por paredes y objetos dentro de la casa.

Esconder y mostrar el techo

Cuando el jugador entra en la casa, queremos ocultar el techo y queremos volver a mostrarlo cuando el jugador la abandone . Para ello, utilizaremos un nodo de Area2D para detectar si el jugador está dentro de la casa o no. Usaremos las señales body_entered() y body_exited() para saber cuándo entra o sale el jugador.

Agregue un nodo Area2D a House . Agregue un nodo CollisionShape2D y establezca una forma de colisión rectangular configurando su propiedad Shape en New RectangleShape2D . Cambie el tamaño de la forma para cubrir el área transitable de la casa como se muestra en la siguiente imagen:

La forma está resaltada en rojo.

Adjunte un nuevo script al nodo House , llámelo House.gd y guárdelo en la carpeta Entitites/House .

Conecte las señales body_entered() y body_exited() de Area2D al nodo House . Las funciones _on_Area2D_body_entered() y _on_Area2D_body_exited() se crearán automáticamente.

Cambia estas dos funciones para ocultar/mostrar el nodo del techo cuando el jugador entra o sale de la casa:

func _on_Area2D_body_entered(body):
	if body.name == "Player":
		$Roof.hide()

func _on_Area2D_body_exited(body):
	if body.name == "Player":
		$Roof.show()

Ahora haz que Roof sea visible haciendo clic en el ícono del Ojo al lado del nombre del nodo y ejecuta el juego para probar si todo funciona como se esperaba.

Usar la cama para descansar

En todos los juegos de rol, el jugador puede usar camas para restaurar la salud y los puntos de maná. Nuestro juego no es una excepción, por lo que el jugador puede decidir dormir presionando el botón de ataque cuando está cerca de la cama .

Comencemos por crear una «pantalla de sueño». Agregue un nuevo nodo ColorRect a CanvasLayer y llámelo Sleep . En el Inspector , establezca Color en negro y Rect → Tamaño en (320,180).

Agregue RichTextLabel a Sleep . Este nodo se usa para mostrar texto con formato y le permite animar el texto con algunas animaciones simples.

En el Inspector , vaya a la sección Fuentes personalizadas y en la propiedad Fuente normal cargue la fuente Font.tres que creamos en el tutorial de GUI . Establezca Rect → Posición en (0,80) y Rect → Tamaño en (320,20), luego desactive la propiedad Clip Content .

Los nodos RichTextLabel admiten el análisis de BBCode para dar formato al texto ( haga clic para conocer todas las etiquetas disponibles ). Entonces, vaya a la sección Código Bb , active la propiedad Habilitado y copie este código en la propiedad Código Bb → Texto :

[center][wave amp=40 freq=2]ZZZ...[/wave][/center]

En este texto usamos dos etiquetas BBCode:

  • [center] : centra el texto dentro de la etiqueta.
  • [wave] : anima el texto haciéndolo subir y bajar. La propiedad amp controla qué tan alto y bajo sube el efecto, y freq controla qué tan rápido sube y baja el texto.

Si está utilizando el código para arrastrar el reproductor con el mouse , recuerde establecer Mouse → Filter en Ignore para los nodos Sleep y RichTextLabel para evitar que bloqueen los eventos del mouse.

Una vez hecho todo, verás el texto animado en la pantalla:

Finalmente, establezca la propiedad modular de Sleep en (255,255,255,0) para que sea invisible. Lo haremos visible nuevamente usando una animación.

Para crear la animación que mostrará esta pantalla, seleccione el nodo Player → AnimationPlayer . El panel Animación se abrirá automáticamente en la parte inferior del editor.

Haga clic en el botón Animación y seleccione Nuevo para crear una nueva animación. Llámalo Sueño .

Agregue una nueva pista de propiedad:

En la primera ventana, elija Dormir como el nodo para animar y en la segunda ventana elija la propiedad modular .

Ahora, establece la duración de la animación en 3 segundos:

Agregue 4 fotogramas clave a la pista de modulación (clic derecho → Insertar clave en la pista) en las posiciones 0, 0.4, 2.6, 3:

Puede usar el control deslizante de zoom en la parte inferior derecha del panel para cambiar la escala de tiempo y facilitarle el trabajo. Si coloca un fotograma clave en el segundo equivocado, puede moverlo arrastrándolo a lo largo de la pista.

Seleccione los dos fotogramas clave centrales haciendo clic en ellos mientras mantiene presionada la tecla Mayús. Luego, en el Inspector cambie Valor a 255,255,255,255.

Ahora los fotogramas clave centrales son blancos.

Finalmente, solo tenemos que agregar al script Player.gd el código para ejecutar la animación y restaurar los valores de salud y maná del jugador. En la función _input() , edite el código que maneja el evento de entrada de ataque de esta manera:

if event.is_action_pressed("attack"):
	# Check if player can attack
	var now = OS.get_ticks_msec()
	if now >= next_attack_time:
		# What's the target?
		var target = $RayCast2D.get_collider()
		if target != null:
			if target.name.find("Skeleton") >= 0:
				# Skeleton hit!
				target.hit(attack_damage)
			if target.is_in_group("NPCs"):
				# Talk to NPC
				target.talk()
				return
			if target.name == "Bed":
				# Sleep
				$AnimationPlayer.play("Sleep")
				yield(get_tree().create_timer(1), "timeout")
				health = health_max
				mana = mana_max
				emit_signal("player_stats_changed", self)
				return
		# Play attack animation
		attack_playing = true
		var animation = get_animation_direction(last_direction) + "_attack"
		$Sprite.play(animation)
		# Play attack sound
		$SoundAttack.play()
		# Add cooldown time to current time
		next_attack_time = now + attack_cooldown_time

La declaración if verifica si el objeto frente al jugador es una cama. Si es así, el código reproduce la animación de Sueño y después de un segundo de retraso ( yield (get_tree().Create_timer(1), “timeout” ) ) restaura los valores de salud y maná a su máximo. Finalmente, se emite la señal player_stats_changed() para actualizar la GUI.

Evitar que los esqueletos entren en la casa

Lo último que queremos hacer es evitar que los esqueletos entren en la casa. Para hacer esto, usamos la señal Area2D body_entered() de la casa para detectar si un esqueleto está entrando por la puerta. Ya hemos creado la función que maneja este evento, así que modifiquémosla así:

func _on_Area2D_body_entered(body):
	if body.name == "Player":
		$Roof.hide()
	elif body.name.find("Skeleton") >= 0:
		body.direction = -body.direction
		body.bounce_countdown = 16

¿Como funciona? Si el cuerpo que entra en contacto con Area2D es un esqueleto, invertimos su dirección de movimiento y establecemos bounce_countdown en 16 para obligarlo a moverse en la nueva dirección durante 4 segundos (el script del esqueleto recalcula la dirección de movimiento cada 250 ms, por lo que 250 ms x 16 = 4000 ms = 4 s).

Conclusiones

En este tutorial aprendimos:

  • Cómo crear una casa cuyos interiores se muestren u oculten automáticamente cuando el jugador entra o sale de ella
  • Cómo crear una cama donde el jugador pueda descansar y recuperar sus puntos de salud y maná
  • Cómo usar el nodo RichTextLabel para crear texto con efectos.