lunes, 15 de diciembre de 2025

Página 125: Apuntes de "Godot 4 3D Platformer Lesson #27: Mecanismos y botones"



Me ha venido un poco de inspiración y he decidido crear un spin-off de la serie de BornCG: una nueva serie de tutoriales centrados en mecanismos.

Estas entradas utilizan el contenido visto previamente como material base mas nuevos modelos que he creado.

Creamos un sistema de botones que abren puertas, con distintos tipos de presión y la opción de mover objetos para activarlos.

Enlace al proyecto en GitHub: https://github.com/ApuntesGodot/apuntesgodot.blogspot.com/tree/main/Mecanismos%20b%C3%A1sicos%20-%20Botones 


Concepto:

Vamos a crear un botón situado en el suelo que abrirá una puerta y permitirá al jugador progresar.

Se crearán dos tipos de mecanismos: unos que requerirán que todos los botones estén presionados y otros que se activarán cuando al menos uno de ellos lo esté.

También diseñaremos dos tipos de botones: uno que permanecerá pulsado tras activarse y otro que necesitará tener un objeto encima para mantenerse presionado.

Por último, añadiremos la función de agarrar ítems del escenario para moverlos y colocarlos sobre los botones que requieren presión constante para mantenerse activados.

Apuntes de como crear los botones y mecanismos:

Para el contexto de estos apuntes, todos los mecanismos, serán escenas AnimaTable3D.

Vamos a crear dos scripts, uno llamado Reciever(Receptor) y Transmitter (Trasmisor), los mecanismos que hagamos,  seran extensión de estas clases.

Reciever.

 

Transmitter 

 

La lógica de este script es mantener separadas las funciones de recepción y emisión.

Con la funcionalidad del botón y la puerta, nos interesa que solo las escenas con las clases Transmitter y Receiver se encarguen de la gestión de los mecanismos.

El programa no fallará si las funciones on_emission_triggered o on_signal_received no existen; en su lugar mostrará un mensaje por consola.

El nuevo script que vamos crear ControlSignal, se encargara de gestiionar la conexión entre emisores y receptores 

 

En este script, se asocia todos los emisores para que puedan enviarle señales a todos los receptores.

Por motivos de evitar fallos, se quitaran todas las posiciones en null de los arrays.

Se forzara a emitir una señal para tener un listado de Transmitter con su valor, cada vez que realiza una emisión, se actualizara su valor. 

En este script se asocian todos los emisores para que puedan enviar señales a todos los receptores.

Existen dos modos, que podemos alternar mediante needAllTransmitterActive:

  • Si está en true, será necesario que todos los Transmitter estén activos para que los Receiver reciban un valor true.

  • En cambio, si está en false, bastará con que uno de los Transmitter esté activo para enviar un true al Receiver

Ahora vamos a trabajar, creando las escenas de la puerta y el botón.

Usaremos la escena Plataforma3x2 como puerta y modificaremos el script para que deje de moverse de punta A a punto B de forma automatica.

 

Podemos seguir usando la escena como plataforma móvil de forma automática estableciendo auto_start en true.

Hemos fragmentado la función move(), dividiendo el movimiento de ida y vuelta en dos nuevas funciones: irPuntoA() e irPuntoB().

También hemos cambiado la extensión a Receiver para habilitar la recepción de señales de los Transmitter.

La función on_signal_received() se encarga de gestionar el valor recibido:

  • Si recibe true, se abrirá la puerta usando irPuntoB().

  • Si recibe false, ejecutará irPuntoA() para cerrarla.

Ahora vamos a crear la escena Botón(AnimaTableBody3D).

 

Esta escena tiene dos meshes, uno presionado y otro sin presionar, se van alternando si son pulsados o no.

 El Area3D solo detectara al Player(1) y los itemAgarrable(7) 

Los itemAgarrable, son todos los ítems que se puede mover por el jugador.

 

 

El RayCast3D lo usaremos solamente para detectar si ya hay algo presionando el botón al iniciar el juego, si el botón no necesita presión para mantenerse activo.

 

Se adjunta captura del script.

 

La mayor parte del código, sobre intercalar Presionado y SinPresionar, ya que no se hacer la animación del botón cuando se pulsa.

En _on_area_3d_body_exited, tenemos $Area3D.get_overlapping_bodies().size() para evitar que cuando pongamos el ItemAgarrable encima del botón, revise que no haya nada presionándolo, sin el cualquier cuerpo que salga, desactivaría el botón.

Emit_Signal, emite también su nombre a modo de identificador, aprovechamos que no pueden haber nodos con el mismo nombre, para que ControlSignal registre su ultima emisión. 

Ahora vamos a crear el itemAgarrable, la escena se llamara LaRoca (AnimarableBody3D).

 

7: ItemsAgarrable.

El nodo Base (Area3D) se encarga de detectar el suelo y los botones.
El objeto se mueve hacia abajo simulando el efecto de la gravedad, y dejará de descender al detectar el suelo o un botón (Es un poco cutre).
El jugador puede atravesar este bloque sin colisionar con él.

7: ItemsAgarrable.
2: Suelo.
6: Botones. 

Adjuntamos script.

 

El código no tiene mucho misterio: en _process() el bloque se mueve hacia abajo, y la señal _on_base_body_entered del nodo Base lo detiene.

Durante las pruebas, noté que Base no siempre detiene el bloque al instante; si está mal posicionado o el bloque se mueve demasiado rápido, puede llegar a atravesar el suelo.

Ahora vamos a ponernos con el Player y a añadirle la función de agarrar ítems.

Vamos agregar tres nodo nuevos:

DetectarItemsAgarrable: Detecta los items que nuestro personaje puede llevar.

PosicionItemAgarrado: Donde pondrá el item cuando el jugador lo transporte.

PuedesAgarrarItem: Solo aparece cuando puedes agarrar el item, es un chivato de cuando el personaje puede tomar el Item, IMPORTANTE: DEJARLO INVISIBLE, se ha dejado visible para que se vea en la captura.

 

Para no complicar más el código base que hemos ido construyendo durante el curso, añadiremos esta funcionalidad como un componente.

Así que vamos a crear un nuevo script.

  

Metemos el componente en la escena y asociamos los nodos a sus propiedades @export.

El personaje guarda temporalmente cualquier ítem que entra en su área en item_en_area, y lo deja en null cuando sale.

Cuando item_en_area tenga un ítem, al pulsar E se moverá a item_agarrado, y en _process() se fijará su posición al Marker3D PosicionItemAgarrado.

Si volvemos a pulsar E, se soltará: item_agarrado pasará a null y dejará de seguir PosicionItemAgarrado.

Al soltar el ítem, ItemAgarrable recuperará su “gravedad” y caerá al suelo. Lo ideal sería soltarlo delante del personaje, no encima; de momento queda pendiente.

Ahora vamos a introducir todas las  

 

Montamos los receptores en el control signal, si quisiéramos montar otro mecanismo, tendríamos que meter otro nodo ControlSignal.

 

Configuramos los botones, el parámetro Esta Presionado no funcionara bien si Necesita Presión esta activado, se volverá a despresionar en cuanto se abra la escena

 

En la puerta debemos definir la posición que tendrá cuando esté cerrada y la posición que tomará al abrirse. Además, debemos desactivar Auto Start para evitar que se abra y cierre en bucle. En la siguiente entrada veremos cómo activar este movimiento mediante un botón.

Y el Player y la LAROCA no parámetros para configurar. 

Estoy convencido de que hay algo que me he dejado, como las mascaras y layers, pero lo importante esta explicado. 


Qué extensa me ha quedado la entrada.

Desde que empecé a ver tutoriales, siempre me quedé con las ganas de encontrar algo sobre este tipo de mecanismos, y parece que me ha tocado hacerlo yo mismo. Estoy seguro de que hay tutoriales específicos, pero la verdad es que no los he buscado.

También me he fijado en que MakerTech es la única youtuber con la que me he cruzado que ha mencionado y enseñado cómo crear componentes. Considero que es muy importante saber cómo hacerlos: como se ve en este tutorial, te evitan tener bloques de código largos, difíciles de entender y mantener, además de ser fácilmente reutilizables.

No hay comentarios:

Publicar un comentario