Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Further Devlog #04: La Nave

La Nave

Siendo uno de los aspectos más importantes del juego, fue también uno de los más sencillos de desarrolla, al menos a en su nivel más básico.

Esta es la organización de la escena de la nave, llamada dentro del motor “Player”:

voy a ir explicando nodo por nodo y su funcionalidad:

  • El Nodo raíz es un Nodo 2D. La gente que inicia en Godot muchas veces se pregunta cual es el mejor nodo para tener como raíz de una escena determinada. Esa pregunta se puede responder fácilmente revisando la documentación acerca de los nodos y su funcionalidad. Muchas veces el mejor nodo es el que te puede dar acceso a la información que requieres para hacer las acciones que planeas. En mi caso, me decante por un simple Nodo2D, ya que no requería nada especializado (Como un CharacterBody2d, por ejemplo) y necesitaba un nodo el cual tuviera acceso a propiedades como la posición global, la rotación, escala, etc .
    • El primer nodo hijo es un AnimatedSprite2D, un nodo sencillo para, como su nombre indica, sprites animados. En este caso lo renombré SkinShip ya que tengo planeado realizar distintos Sprites animados que servirán de Skins para la nave.
    • El Siguiente nodo es un Sprite2D, es similar al anterior, salvo que no soporta animaciones. Este Nodo es perfecto para cuando tienes un Sprite Sheet con varias imágenes estáticas que representan objetos que tienen comportamientos similares, por lo que puedes crear una escena que tenga un determinado comportamiento (como por ejemplo coleccionables que desaparecen al entrar en contacto con el jugador) y con facilidad, ya sea manualmente o por código, cambiar el Sprite al objeto que desees. En mi caso solo lo uso para tener un overlay (superposición) que representa visualmente la habilidad de intangibilidad de la nave.
    • El siguiente Nodo es un Area2D llamada Hurtbox. Las Area2D sirven, como su nombre indica, como un area que monitoriza cuando algún objeto, sea otra área, un cuerpo o incluso el cursor del mouse, entra en contacto con ellas. Esta en particular la uso para detectar el daño a la nave.
      • Como se puede ver, tiene como nodo hjio un CollisionPolygon2D, que representa el área en la cual se monitoriza la entrada en contacto con otros objetos.
    • El siguiente nodo es un Timer, uno de los más útiles y geniales de Godot. Su funcionalidad es sencillisima, una cuanta hasta cero, pero es que en el desarrollo de juegos se usa mucho el tiempo para hacer que sucedan cosas, y tener un Nodo especifico para eso, que además se puede invocar mediante código de manera increíblemente fácil, es muy útil. este Timer en particular es el Abilitytimer, y controla el tiempo en que la habilidad de la nave permanece activa.
    • Los siguientes tres nodos son Nodos personalizados creados a partir de componentes. En Godot es posible crear tus propios Nodos simplemente creando un script y, al principio de todo, colocar “class_name NombreDelNodo”, posteriormente, después de guardar el script, si buscas el nombre del nodo que creaste entre los nodos que se pueden agregar, lo podrás conseguir ¡Es demasiado genial!.
      • El primer nodo lo cree yo desde cero, es un nodo que guarda las “estadísticas” (Osea, las características) de la Nave: Cuantos puntos recolecta, cuanto combustible quema, su resistencia, etc.
      • El segundo y tercer nodo están basados en los componentes creados por el youtuber Heartbeast (si sabes ingles, recomiendo muchísimo su canal). El “Flash Component” aplica un shader que vuelve blanca a la nave por un corto periodo de tiempo cuando recibe daño, el “Shake Component” hace que el Sprite de la nave se agite cuando recibe daño.
    • El siguiente nodo es otra Area2D, pero en este caso es para detectar dos cosas: cuando el mouse (o el dedo) del jugador entra en el área y cuando el área entra en contacto con los coleccionables. Hice que hiciera las dos cosas para: 1) Ahorrar recursos y 2) Como es un área bastante grande, y siempre es bueno que el área de recolección sea mayor al de daño, lo vi bastante conveniente.
      • A diferencia del Area2D anterior, esta tiene como nodo hijo un CollisionShape2D. La principal diferencia entre un CollisionShape2D y un CollisionPolygon2D es que el primero usa figuras geométricas fijas (cuadrados o rectángulos, círculos, etc), mientras que el segundo consta de figuras poligonales que puedes crear libremente.
    • Los siguientes 3 nodos son TextureProgressBar, barras de progreso texturizadas. Son nodos que pertenecen al tipo de nodo llamado “Control”, los mejores nodos jamas hechos. estos nodos en particular sirven para darle al jugador una indicación de:
      • Cuanto combustible queda en la nave
      • Cuantos puntos lleva acumulados
      • Cuanta resistencia le queda. Estos nodos son lo máximo ya que automatiza y simplifica algo que, quizá en otro motor, hubiese tenido que crear yo desde cero
    • El siguiente nodo es un Label, otro tipo de nodo de control. Este es dolorosamente sencillo, simplemente es para mostrar texto en pantalla, en mi caso lo uso para mostrarle al jugador el nivel de la nave.
    • El último nodo es quizá temporal, es solo un pequeño icono creado utilizando un nodo Polygon2D. Lo hago cambiar de color para demostrar cuando se puede o no utilizar la habilidad de la nave.

Asi es como se ve la escena general de la nave, en la imagen se indica que hace cada cosa, pero igual explicare un poco como funciona.

Lo primero, notaras que estoy colocando todo lo que es el HUD en la propia nave, eso es porque mientras uno esta jugando, siempre vas a tener la vista puesta en la nave, por lo que colocar información tan critica como cuanto combustible queda o si se puede realizar la habilidad en un HUD aparte en otra punta de la pantalla puede distraer al jugador y hacerle caer en un obstáculo. El HUD consta, de arriba a abajo, de 3 barras: dos semi circulares para el combustible y los puntos, y una recta para la resistencia (con un máximo de 5). Por el momento el juego asume que el jugador es diestro, eso lo cambiaré más adelante, pero por el momento a la izquierda están los indicadores del nivel de la nave y cuando la habilidad está disponible. La Hurtbox es la figura poligonal que se ve dentro del sprite de la nave y la MainArea es la capsula azul que envuelve casi todo.

El script de la nave consta de 14 funciones: las dos primeras siendo funciones por defecto de Godot, las siguientes 8 son funciones creadas por mi para el funcionamiento de la nave y las últimas 4 son funciones asociadas a señales.

Empezando con las creadas por mí:



func motion(d):

# Para el movimiento de la Nave

	if Input.is_action_pressed("Touch") and CanMove:
		global_position = lerp(global_position, get_global_mouse_position(), 30 * d)
		
	if Input.is_action_just_released("Touch"):
		CanMove = false


func margin():

# Para que la nave no salga de la pantalla y se pierda de vista
	
	global_position.x = clamp(global_position.x, MinSide.x + Margin.x, MaxSide.x - Margin.x)
	global_position.y = clamp(global_position.y, MinSide.y + Margin.y, MaxSide.y - Margin.y)


func fuel_bar():

# Controla la barra de combustible, indica cuanto se pierde por segundo, 
# que colores tomar según la cantidad restante y que hacer cuando se acaba.

	FuelBar.value -=  ShipStats.FuelCombustion
	
	if FuelBar.value > 50:
		FuelBar.tint_progress = Color.GREEN
		
	elif FuelBar.value < 50 and FuelBar.value > 20:
		FuelBar.tint_progress = Color.ORANGE
		
	elif FuelBar.value < 20:
		FuelBar.tint_progress = Color.RED
		
	elif FuelBar.value == 0:
		get_parent().game_over()

func points_bar():

# controla qué ocurre si la barra de puntos llega a 100
	
	if PointsBar.value == 100:
		PointsBar.value = 0
		Master.velocity += 50
		Master.speed_up()
		Master.ShipLevel += 1

func kinetic_ability():

# Aqui se evalua si se puede realizar la habilidad de la nave

	if round(Difference.y) >= AbilityThreshold and CanMove and CanAbility:
		intangibility(true)
		CanAbility = false

func intangibility(CanDo):

# Aqui se controla todo con respecto a la habilidad de la nave

	if CanDo:
		Overlay.visible = true
		Hurtbox.monitoring = false
		AbilityTimer.start()
	else:
		Overlay.visible = false
		Hurtbox.monitoring = true

func sides():

# Este es para uso estetico. Según la dirección de la nave, el fondo parallax 
# cambia su dirección horizontal

	if PastPos.x > CurrentPos.x and CanMove:
		movin_left.emit()
	elif PastPos.x < CurrentPos.x and CanMove:
		movin_right.emit()

func get_hurt():

# Aqui se controla qué ocurre al recibir daño

	Flash.flash()
	Shake.tween_shake()
	ShipStats.Resistance-= 1

Las señales, por su parte, son estas:

func _on_main_area_mouse_entered():

# Esta Señal evalua si el mouse ha entrado al MainArea. De ser así,
# vuelve la variable "CanMove" en verdadera y permite a la nave moverse

	CanMove = true

func _on_main_area_area_entered(area):

# Aqui se controla la segunda función de la MainArea, que es recolectar los 
# coleccionables.
# En este caso los coleccionables no tienen ningun valor por si mismo, solo un tipo.
# Segun el tipo, se adquiere un valor al azar entre 1 y el rango de recolección de ese
# coleccionble segun las estadisticas de la nave.

		
	if area.is_in_group("collectables"):
		if area.CollType == 0:
			var sum = randi_range(1,ShipStats.FuelRange) * 10
			FuelBar.value += sum
		elif area.CollType == 1:
			var sum = randi_range(1,ShipStats.PointsRange) 
			PointsBar.value += sum
			
		area.queue_free()

func _on_ability_timer_timeout():

# Cuando la habilidad de la nave es usada, el Ability Timer se activa.
# Cuando se acaba el tiempo, la habilidad se cancela.

	intangibility(false)
	CanAbility = false
	
# Esta parte es muy interesante: para evitar que el jugador abuse de la habilidad,
# se hace un pequeño "Cool down" de un segundo utlizando un Timer "desechable" que 
# se invoca por medio de codigo. Se le dice al programa que espere a que este Timer
# desechable termine para volver a utilizar la habilidad. Es muy genial y fácil de hacer.

	var waitTime = get_tree().create_timer(1)
	await waitTime.timeout
	CanAbility = true

func _on_hurtbox_area_entered(area):

# Por último, aquí se controla lo que sucede cuando la Hurtbox entra en contacto con un area de tipo "Obstacle" (Obstaculo)

	if area.is_in_group("obstacle"):
		get_hurt()

Las ultimas dos funciones son la “_ready()” y la “_process(delta)” vienen por defecto en Godot:


func _ready():

# Por el momento uso el _ready solo para hacer que el tiempo del AbilityTimer sea igual
# Al de las estadisticas de la nave.

	AbilityTimer.wait_time = ShipStats.AblityTime


func _process(delta):

# En la funcion _process es donde ocurre todo lo que debe ocurrir durante el juego


# Aqui se cambia el texto del Label que indica el nivel de la nave
	LevelLabel.text = str(Master.ShipLevel)
	
# Aqui se reproduce la animación de la nave
	SkinShip.play()
	
# La variable "Difference" se usa para poder activar la habilidad de la nave
	Difference = PastPos - CurrentPos
	
# Esta condicion es para desactivar la función del combustible de la nave cuando se entra
# a una boss fight
	if not Master.IsInBoss:
		fuel_bar()
	
# Aqui se controla el color del icono que indica cuando se puede realizar la Habilidad.
	if CanAbility:
		abilityIcon.color = Color.BLUE
	else:
		abilityIcon.color = Color.RED
	
# El valor de la barra de resistencia segun la resistencia que queda
	LivesBar.value = ShipStats.Resistance
	
# Si la resistencia se va a cero, game over
	if ShipStats.Resistance == 0:
		get_parent().game_over()
	
# Aqui se llama a todas las funciones anteriormente mencionadas
	kinetic_ability()
	motion(delta)
	sides()
	points_bar()
	margin()
	
# De estas variables se saca el valor de la variable "Diferrence".

	PastPos = lerp(CurrentPos,global_position, 30 * delta)
	CurrentPos = global_position

Me gustaría mostrar las variables, pero esta entrada ya se está haciendo muy larga. La próxima hablaré de los módulos.

Support this post

Did you like this post? Tell us

Leave a comment

Log in with your itch.io account to leave a comment.