Patrones de diseño en Godot: hoja de ruta rápida

Publicado el:
Tiempo de lectura: 10 min
by
JuancaDev

Al momento de crear un videojuego, sin importar el motor o lenguaje escogido, uno de los aspectos más importantes es el correcto funcionamiento de los sistemas que lo componen y la manera en la que estos interactúan entre sí. Para lograrlo, es clave tener una buena arquitectura de software, y una de las herramientas más usadas para esto son los patrones de diseño. Estos patrones son soluciones probadas a problemas comunes en el desarrollo de software y nos ayudan a crear sistemas más mantenibles, escalables y comprensibles, sin importar si estamos trabajando en un pequeño proyecto indie o en un gran juego AAA, si es un solo desarrollador o un equipo grande. Con el pasar del tiempo los proyectos tienden a crecer en complejidad y tamaño, y los patrones de diseño nos ayudan a manejar esa complejidad de manera efectiva.

En esta ocasión te hablaré acerca de 7 patrones de diseño muy conocidos y usados en la programación a nivel general y cómo, desde mi punto de vista, pueden ayudar en el desarrollo incluso en Godot. Pero, ¿por qué específicamente en Godot? La respuesta es simple: GDScript es un lenguaje de programación diseñado específicamente para el desarrollo de videojuegos, y Godot como motor es altamente flexible y extensible, lo que permite implementar patrones de diseño de manera efectiva para mejorar la arquitectura de los juegos.


Patrones de diseño en Godot: hoja de ruta rápida

  • 01-singleton-autoload/
  • 02-observer-signals/
  • 03-factory/
  • 04-abstract-factory/
  • 05-strategy/
  • 06-decorator/
  • 07-composite/

Si quieres ir directo al código o recibir notificaciones, mantente atento al blog y al repositorio en GitHub. Dale star para recibir actualizaciones.

¡Es completamente gratis! 🎉


🎯 Disclaimer

Antes de continuar este post y los consecuentes son mi interpretación personal de los patrones de diseño aplicados a Godot y GDScript. No son la única forma de implementarlos, ni necesariamente la “correcta”. La idea es compartir ideas prácticas y ejemplos que puedan inspirar tu propio enfoque.


🔍 Por qué importan los patrones

Los patrones no son recetas mágicas, pero sí un lenguaje común para diseñar software que evoluciona sin romperse. Aunque aquí los aterrizamos en Godot, su valor es transversal:

  • Comunicación: al mencionar “Observer” o “Factory”, la gran mayoría de desarrolladores saben a qué te refieres.
  • Mantenibilidad: separan responsabilidades y reducen acoplamiento, lo que hace refactors y correcciones menos riesgosos.
  • Escalabilidad: permiten agregar casos nuevos (enemigos, UI, items) sin reescribir lo existente.
  • Onboarding: un dev nuevo reconoce patrones conocidos y navega el código más rápido.
  • Testing: al aislar dependencias, las pruebas unitarias o de integración se vuelven más sencillas.

Lo importante es usarlos con criterio: el patrón se adopta cuando clarifica o estabiliza el diseño, no por moda. ¿A qué me refiero con esto? A que no es necesario forzar un patrón si la solución simple funciona bien. La clave está en balancear simplicidad y estructura.


Ahora veamos un resumen rápido de qué es o para qué sirve cada patrón y cómo o dónde lo implementaría yo en Godot.

01) Singleton

Un servicio global, es decir, un solo servicio para todo, solo debe existir una vez en toda la aplicación. El ejemplo más común cuando estamos aprendiendo a crear videojuegos es el GameController o GameManager, que se encarga de manejar el estado global del juego como la puntuación, vidas, niveles, etc.

Pero no necesitamos algo global para que sea un singleton. Podemos crear un singleton para un boss de un nivel, o para manejar todos los ítems que contiene un nivel o un mapa. Claro está, no es la única forma de usar singleton, pero es una de las más comunes. Godot facilita la implementación de singleton a través de los autoloads, pero si por error creamos múltiples instancias de un singleton podemos tener problemas, por lo que es importante asegurarnos de que solo exista una instancia.

02) Observer

Se podría decir que el patrón Observer es nativo en Godot, ya que las señales (signals) permiten notificar cambios de estado o enviar eventos sin necesidad de acoplar scripts directamente. Esto funciona muy bien cuando están en la misma escena, pero ¿qué pasa cuando queremos comunicar eventos entre escenas o nodos que no están relacionados directamente? Aquí es donde podemos implementar un EventBus o un sistema de eventos centralizado. Además, nos evita búsquedas costosas en el árbol de nodos. Un EventBus puede ser un singleton que maneje todas las señales globales del juego, permitiendo a cualquier nodo suscribirse o emitir eventos sin conocer a los demás.

03) Factory

Este patrón tan conocido se utiliza para la creación de objetos sin exponer la lógica de creación al cliente. Se refiere a una clase o método que se encarga de crear instancias de otras clases, evitando que el código que las utiliza tenga que conocer los detalles de cómo se crean esos objetos. En videojuegos, a nivel general, es muy útil para crear enemigos o ítems de manera dinámica según el contexto del juego.

La factory puede generar diferentes elementos como balas o enemigos basados en parámetros de entrada. Desde mi punto de vista, uno de los géneros de juegos donde este patrón es más útil es en los juegos tipo rogue-like o rogue-lite, donde los niveles, enemigos e ítems son generados de manera procedural, creando enemigos o ítems según la dificultad del nivel, el progreso del jugador o eventos aleatorios.

04) Abstract Factory

Este patrón es similar a Factory pero no igual. Con esto me refiero a que Abstract Factory se utiliza para crear familias de objetos relacionados o dependientes sin especificar sus clases concretas. Es decir, mientras que Factory crea objetos individuales, Abstract Factory crea conjuntos de objetos que pertenecen a una misma familia. Y tomando como ejemplo, desde mi punto de vista el juego que mejor aplica este concepto es Mario Maker 2, donde tenemos diferentes temas o biomas (mundo hielo, mundo fuego, mundo desierto, etc.) y cada uno tiene sus propios enemigos, decoraciones y mecánicas. Si seleccionamos un bioma o un estilo visual, todos los elementos que creemos (enemigos, plataformas, decoraciones) van a coincidir con ese estilo.

05) Strategy

Este patrón permite definir familias de algoritmos, encapsular cada uno y hacerlos intercambiables. Permite que el algoritmo varíe independientemente de los clientes que lo utilizan. En videojuegos es muy útil para definir diferentes comportamientos o estrategias para personajes no jugables (NPCs) o enemigos. Por ejemplo, un enemigo puede tener diferentes patrones de ataque (cuerpo a cuerpo, a distancia, magia) y podemos cambiar su estrategia en tiempo de ejecución según el contexto del juego (distancia al jugador, salud restante, etc.). En teoría es un patrón muy fácil de entender, pero en la práctica puede ser un poco complicado de implementar correctamente, ya que requiere una buena planificación y diseño previo.

06) Decorator

Con este podemos añadir funcionalidades o comportamientos adicionales a un objeto de manera dinámica sin alterar su estructura original. En videojuegos es útil para aplicar efectos temporales o mejoras a personajes o ítems. El uso más común son los power-ups o buffs que modifican las habilidades del jugador (aumento de velocidad, fuerza, resistencia) sin cambiar el código base del personaje. Con esto podemos crear una cadena de decoradores que se apliquen secuencialmente, permitiendo combinaciones flexibles de efectos; incluso podríamos generar un árbol de habilidades o perks para el jugador.

07) Composite

Trata de componer objetos en estructuras de árbol para representar jerarquías parte-todo. Permite a los clientes tratar objetos individuales y composiciones de objetos de manera uniforme. En videojuegos es útil para manejar escenas o niveles complejos donde los objetos pueden contener otros objetos. Este parece un poco más complejo de entender solo con la teoría, pero si vamos a la práctica podríamos diseñar un enemigo que a su vez está compuesto por varias partes, como un jefe final con varias cabezas o extremidades. Cada una debe ser eliminada de manera individual para derrotar al jefe completo, pero al mismo tiempo el jefe actúa como una sola entidad en el juego, pudiendo moverse, atacar y demás acciones de manera conjunta.

Conclusión

Los patrones de diseño son herramientas poderosas para mejorar la arquitectura y mantenibilidad de nuestros proyectos a nivel general, pero para videojuegos y específicamente en Godot pueden marcar una gran diferencia en la calidad y escalabilidad de nuestros juegos. Al entender y aplicar estos patrones de manera efectiva, podemos crear sistemas más robustos, flexibles y fáciles de mantener a lo largo del ciclo de vida del desarrollo del juego.


¡Y como siempre, feliz coding! 🚀✨

Si ves algo para mejorar, abre un issue o envíame un mensaje.