La implementación de patrones de diseño de programación de juegos comunes en su proyecto Unity puede ayudarle a construir y mantener de manera eficiente una base de código limpia, organizada y legible. Los patrones de diseño reducen el tiempo de refactorización y pruebas, aceleran los procesos de desarrollo y contribuyen a crear una base sólida para hacer crecer su juego, su equipo y su negocio.
Piense en los patrones de diseño no como soluciones acabadas que puede copiar y pegar en su código, sino como herramientas adicionales que pueden ayudarle a crear aplicaciones más grandes y escalables.
Esta página explica el patrón de diseño de fábrica.
El contenido se basa en el libro electrónico gratuito, Mejora tu código con patrones de programación de juegos.
Echa un vistazo a más artículos de la serie de patrones de diseño de programación de juegos Unity en el centro de mejores prácticas de Unity o a través de estos enlaces:
A veces es útil tener un objeto especial que cree otros objetos. Muchos juegos generan una gran variedad de cosas a lo largo del juego, y a menudo no sabes lo que necesitas en tiempo de ejecución hasta que realmente lo necesitas.
El patrón de fábrica designa un objeto especial llamado - lo has adivinado - una fábrica para este propósito. En un nivel, encapsula muchos de los detalles implicados en la creación de sus "productos". El beneficio inmediato es la reducción del código.
Sin embargo, si cada producto sigue una interfaz común o una clase base, puedes dar un paso más y hacer que contenga más de su propia lógica de construcción, ocultándola de la propia fábrica. La creación de nuevos objetos se hace así más extensible.
También puede subclasificar la fábrica para crear múltiples fábricas dedicadas a productos específicos. Hacer esto ayuda a generar enemigos, obstáculos o cualquier otra cosa en tiempo de ejecución.
En GitHub hay disponible un proyecto de muestra que demuestra diferentes patrones de diseño de programación en el contexto del desarrollo de juegos, incluido el patrón de fábrica.
La muestra del patrón de fábrica consiste en código para que un jugador se mueva por un laberinto. En el laberinto, puedes generar dos GameObjects diferentes llamados productos haciendo clic. Ambos utilizan la misma interfaz y comparten una forma similar, pero uno genera partículas y el otro reproduce un sonido.
La escena del patrón de fábrica está en la carpeta "6 Factory".
Imagina que quieres crear un patrón de fábrica para instanciar elementos para un nivel de juego. Usted puede utilizar Prefabs para crear GameObjects, pero también puede ser que desee ejecutar algún comportamiento personalizado al crear cada instancia.
En lugar de utilizar sentencias if o un switch para mantener esta lógica, cree una interfaz llamada IProduct y una clase abstracta llamada Factory como se indica en el ejemplo de código.
Los productos deben seguir una plantilla específica para sus métodos, pero por lo demás no comparten ninguna funcionalidad. Por lo tanto, se define la interfaz IProducto.
Las fábricas pueden necesitar alguna funcionalidad común compartida, por lo que este ejemplo utiliza clases abstractas. Ten en cuenta la sustitución de Liskov de los principios SOLID cuando utilices subclases. Establece que los objetos de una superclase deben poder sustituirse por objetos de una subclase sin que ello afecte a la corrección del programa. En otras palabras, cualquier programa que utilice una referencia a una superclase debería poder utilizar cualquiera de sus subclases sin saberlo.
La interfaz IProducto define lo que es común entre sus productos. En este caso, simplemente tiene una propiedad ProductName y cualquier lógica que el producto ejecute en Initialize.
A continuación, puede definir tantos productos como necesite(ProductoA, ProductoB, etc.) siempre que sigan la interfaz IProducto.
La clase base, Factory, tiene un método GetProduct que devuelve un IProduct. Es abstracto, por lo que no se pueden crear instancias de Factory directamente. Se derivan un par de subclases concretas(ConcreteFactoryA y ConcreteFactoryB), que obtendrán los diferentes productos.
GetProduct en este ejemplo toma una posición Vector3 para que pueda instanciar un GameObject Prefab más fácilmente en una ubicación específica. Un campo en cada fábrica de hormigón también almacena la plantilla Prefab correspondiente.
El resultado es una estructura parecida a la de la imagen superior.
En el fragmento de código puedes ver un ejemplo de ProductA y ConcreteFactoryA.
Aquí, has hecho que las clases de producto MonoBehaviours que implementan IProduct aprovechen Prefabs en la fábrica.
Observe cómo cada producto puede tener su propia versión de Inicializar. El ejemplo ProductA Prefab contiene un ParticleSystem, que se reproduce cuando ConcreteFactoryA instantiza una copia. La fábrica en sí no contiene ninguna lógica específica para activar las partículas; sólo invoca el método Initialize, que es común a todos los productos.
Explore el proyecto de ejemplo para ver cómo el componente ClickToCreate cambia entre fábricas para crear ProductA y ProductB, que tienen comportamientos diferentes. El ProductoB emite un sonido cuando aparece, mientras que el ProductoA activa un efecto de partículas para ilustrar el concepto central de las variaciones de producto.
El patrón de fábrica le resultará muy útil para configurar muchos productos. La definición de nuevos tipos de productos en su aplicación no modifica los existentes ni requiere que modifique el código anterior.
Separar la lógica interna de cada producto en su propia clase mantiene el código de la fábrica relativamente corto. Cada fábrica sólo sabe invocar Initialize en cada producto sin estar al tanto de los detalles subyacentes.
El inconveniente es que hay que crear una serie de clases y subclases para implementar el patrón. Al igual que los otros patrones, esto introduce un poco de sobrecarga, que puede ser innecesaria si usted no tiene una gran variedad de productos. Por otro lado, el tiempo inicial dedicado a configurar las clases puede ser positivo a largo plazo en términos de desacoplamiento del código y facilitar su mantenimiento.
La implementación de la fábrica puede variar mucho de lo que se muestra aquí. Tenga en cuenta los siguientes ajustes cuando construya su propio patrón de fábrica:
Utiliza un diccionario para buscar productos: Es posible que desee almacenar sus productos como pares clave-valor en un diccionario. Utilice un identificador de cadena único (por ejemplo, el Nombre o algún ID) como clave y el tipo como valor. Esto puede hacer más cómoda la recuperación de productos y/o sus correspondientes fábricas.
Hacer que la fábrica (o un gestor de fábrica) sea estática: Esto facilita su uso, pero requiere una configuración adicional. Las clases estáticas no aparecerán en el Inspector, por lo que tendrá que hacer que su colección de productos también sea estática.
Aplícalo a objetos que no sean GameObjects y a comportamientos que no sean MonoBehaviours: No se limite a los prefabricados u otros componentes específicos de Unity. El patrón de fábrica puede funcionar con cualquier objeto de C#.
Combinar con el patrón de reserva de objetos: Las fábricas no necesitan necesariamente instanciar o crear nuevos objetos. También pueden recuperar los existentes en la jerarquía. Si estás instanciando muchos objetos a la vez (por ejemplo, proyectiles de un arma), utiliza el patrón object pool para una gestión de memoria más optimizada.
Las fábricas pueden generar cualquier elemento de juego en función de las necesidades. Sin embargo, la creación de productos no suele ser su única finalidad. Puede que estés utilizando el patrón de fábrica como parte de otra tarea mayor (por ejemplo, configurar elementos de interfaz de usuario en un cuadro de diálogo de partes de un nivel de juego).
Encontrarás más consejos sobre cómo utilizar patrones de diseño en tus aplicaciones Unity, así como los principios SOLID, en el libro electrónico gratuito Level up your code with game programming patterns.
Puede encontrar todos los libros electrónicos y artículos técnicos avanzados de Unity en el centro de mejores prácticas. Los libros electrónicos también están disponibles en la página de mejores prácticas avanzadas de la documentación.