Me parece buena idea dejar de enumerar cada proyecto con su propio «devlog», quizás sea mejor referirme a lo que estoy desarrollando en el momento, con un diario único.
En redes sociales compartí hace muchísimo tiempo (más del que tenía previsto) las imágenes de un proyecto que vengo arrastrando desde entonces. Un juego de naves. ¿Genérico? Tal vez.
Como todos los proyectos con fines de aprendizaje, éste también me ha ayudado a descubrir cosas nuevas con el motor. Entre ellas está la elaboración de pequeñas herramientas «plugins» para agregar funcionalidad a Godot y un menú de inicio. ¿Pueden creer que nunca había diseñado un menú de inicio?
Sé que pueden sorprenderse por la cantidad de cosas que todavía no hago, pero es de esperar, puesto que sólo soy un aficionado. En las palabras de hoy les hablaré sobre el estado de este sencillo proyecto.
Cosas nuevas para mi 📖️
Menú principal 1️⃣️

Fue súper interesante hacer el cambio de paneles para cada sección del menú. Había pensado en usar simplemente una imagen de fondo movida con las letras en el centro… pero me gustó más esta variante.
Todavía no está terminado, pero a partir de aquí no es tan complicado seguir. Les mostraré cómo funciona:

Cada sección o «ventana» es un nodo TextureRect y dentro de ellos estará toda la información. Para cambiar qué ventana se muestra, el nodo «Windows_container» tiene el siguiente script:
extends Control onready var window_current = $window_start func change_window(new_window) -> void: if !new_window.anim.is_active(): new_window.window_in() window_current.window_out() window_current = new_window func _on_btn_next_window_pressed(): move_child(window_current, window_current.get_position_in_parent() + get_child_count() - 1) for window in get_children(): if window.get_position_in_parent() == 0: change_window(window) break func _on_btn_last_window_pressed(): for window in get_children(): if window.get_position_in_parent() == get_child_count() - 1: move_child(window, 0) change_window(window) break func _on_ButtonOrangeBigup_pressed(): get_tree().change_scene("res://Game.tscn")
Hay una variable llamada «window_current» que hace un «get_node()» ( get_node es lo mismo que $) para seleccionar la primera ventana que se muestra en pantalla.
Cada vez que se cambia la escena llamo a una función con la dirección del nuevo nodo, y así actualiza el valor de la variable window_current.
Lo interesante es aprender lo que pasa cuando presionamos el botón de izquierda o derecha. En el caso de este último, lo que hago es mover el nodo que está dentro de «window_current» a la última posición dentro de los hijos del nodo Windows_container. Veamos el comportamiento en vivo:
En caso de que presionemos el botón izquierdo hago lo contrario, y muevo la ventana que se encuentra en la última posición, a la primera (la primera posición es la 0).
Patrón en base del tiempo para los enemigos 2️⃣️

Este es un avance que me gusta, pero no lo considero eficiente todavía. Creo que sería mejor que el patrón del enemigo se siga en función de la distancia recorrida, lo que no es muy difícil de implementar, estoy en ello.
Veamos lo que llevo hasta el momento: estas son las variables que determinan la dirección:
onready var direction_change_timer: Timer = $change_direction enum direction_states { LEFT, RIGHT, UP, DOWN, LEFT_DOWN, RIGHT_DOWN, LEFT_UP, RIGHT_UP } var direction_state_current = direction_states.RIGHT_DOWN var patron_array: Array = [direction_states.RIGHT_DOWN, direction_states.LEFT, direction_states.DOWN, direction_states.LEFT_UP, direction_states.RIGHT] var patron_index: int = 0
Prácticamente es una máquina de estados que va cambiando en el orden que hay dentro del patrón. Tengo una idea para optimizar esto, y viene dada con el siguiente gran punto que estoy estudiando gracias a este proyecto… ya lo comentaré.
Antes de terminar faltaría analizar una parte del código donde se efectúa el cambio de estado, de la nave:
func _on_change_direction_timeout(): if patron_index >= patron_array.size() - 1: patron_index = 0 direction_state_current = patron_array[patron_index] patron_index += 1 match direction_state_current: direction_states.LEFT_DOWN: direction = Vector2(-1, 1) direction_states.RIGHT_DOWN: direction = Vector2(1, 1) direction_change_timer.wait_time = 1.4 direction_states.LEFT_UP: direction = Vector2(-1, -1) direction_change_timer.wait_time = 1.4 direction_states.LEFT: direction = Vector2(-1, 0) direction_change_timer.wait_time = 1 direction_states.RIGHT: direction = Vector2(1, 0) direction_change_timer.wait_time = 1 direction_states.DOWN: direction = Vector2(0, 1) direction_change_timer.wait_time = 0.8 direction_states.UP: direction = Vector2(0, -1) direction_change_timer.start()
Cada vez que termina el tiempo de transición entre cada estado, se va sumando un número al índice del patrón. Empieza en 0 y termina cuando el índice es mayor a la cantidad de elementos que hay en la lista de acciones (patrón).
Aprendiendo a producir pequeños plugins 3️⃣️
Se me ocurrió que en este proyecto podía estudiar de una vez por todas cómo funciona la creación de plugins. En la documentación de Godot hay varias entradas sobre el tema, que les dejaré aquí:
El caso es el siguiente: los niveles de este juego tienen diferentes estados. Un estado de asteroides, otro estado para la aparición de los enemigos y un tercero para otro tipo de enemigo o «jefe».
Esto se puede conseguir editando duplicando la escena de un nivel y personalizando su script principal, con variables «export», por ejemplo. Sin embargo, tal vez usando una pequeña herramienta más visual, pueda automatizar la creación de estos niveles con los estados que sean necesarios.
No estoy seguro de que sea la mejor solución, pero hasta ahora esto es lo que llevo:
Se pueden agregar nodos… sé que es muy poco ;-;. Pero es un avance ;).
Dejaré para otra nota los avances con el enemigo final. Todavía estoy tratando de conseguir un buen efecto de láser con shaders. ¡Muchas gracias a los que siguen estos diarios!
Uffff es tremendo el trabajo que se necesita para un minijuego, es muy esclarecedor leer estos devlogs. Gracias
Gracias a ti por leer 🙂