Extensión de Unity para la creación de HUDs modulares
Abstract
[ES] En este proyecto se crea una extensión o plugin para el motor de videojuegos Unity3D que facilita y acelera la creación de interfaces en la partida (comúnmente llamadas HUDs o heads-up displays), además de una serie de elementos listos para ser añadidos a ellas. Se implementa también una librería de clases que provee de funcionalidad al conjunto, acompañada de una API que permite su utilización de forma directa y sencilla. Adicionalmente, se desarrolla un juego completo con el que mostrar el uso de la extensión.
Full text
ESCOLA TÈCNICA SUPERIOR D'ENGINYERIA INFORMÀTICA UN I V E R I S T A T P O L I T È C N I C A D E V A L È N C I A Extensión de Unity para la creación de HUDs modulares T r a b a j o F i n d e G r a d o Grado en Ingeniería Informática Autor: Armando Miguel Díaz Bruñó Tutor: Javier Lluch Crespo 2014 - 2015
Extensión de Unity para la creación de HUDs modulares 2
RESUMEN En este proyecto se crea una extensión o plugin para el motor de videojuegos Unity3D que facilita y acelera la creación de interfaces en la partida (comúnmente llamadas HUDs o heads-up displays), además de una serie de elementos listos para ser añadidos a ellas. Se implementa también una librería de clases que provee de funcionalidad al conjunto, acompañada de una API que permite su utilización de forma directa y sencilla. Adicionalmente, se desarrolla un juego completo con el que mostrar el uso de la extensión. Palabras clave: extensión, plugin, Unity, interfaces, HUD, videojuego RESUM Al present projecte es crea una extensió o plugin per al motor de videojocs Unity3D que facilita i accelera la creació d'interfícies dins la partida (comunament anomenades HUDs o heads-up displays), a més d'una sèrie d'elements preparats per ser afegits a aquestes. S'implementa també una llibreria de classes que proveeix de funcionalitat al conjunt, acompanyada d'una API que permet la seua utilització de manera directa i senzilla. Addicionalment, es desenvolupa un joc complet amb el qual mostrar l'ús de l'extensió. Paraules clau: extensió, plugin, Unity, interfícies, HUD, videojoc ABSTRACT In this project, an extension or plugin for the videogame engine Unity3D that eases and speeds the creation of interfaces inside the game (commonly called HUDs or heads-up displays) is created, besides a number of elements ready to be added to them. Moreover, a library of classes that provide funcionality to the whole set is implemented, along with an API that allows its utilization in a direct and simple way. Additionally, a complete game with which show the use of the extension is developed. Keywords: extension, plugin, Unity, interfaces, HUD, videogame 3
Extensión de Unity para la creación de HUDs modulares 4
AGRADECIMIENTOS Al equipo de WildFrame Media, porque son incapaces de no ayudar. A mi tutor, Javier Lluch, porque él sí me ha dado cuartelillo. A Aitor, por ofrecerse a crear el excelente sonido del juego. A Meritxell, por acompañarme en tantas tardes y noches de trabajo. 5
Extensión de Unity para la creación de HUDs modulares 6
ÍNDICE DE CONTENIDOS 1. Introducción..................................................................................................................11 2. Objetivos........................................................................................................................13 2.1. Extensión del Unity Editor..................................................................................13 2.2. Controlador y elementos de interfaz................................................................13 2.3. Desarrollo de un juego........................................................................................14 3. Estado del arte..............................................................................................................15 3.1. Motores gráficos populares..............................................................................18 3.2.1. Unreal Engine................................................................................................18 3.1.2. Unity3D...........................................................................................................20 3.1.3. CryEngine.......................................................................................................22 4. Herramientas utilizadas..............................................................................................25 5. Características de Unity3D.........................................................................................27 5.1. El motor.................................................................................................................27 5.1.1. Las escenas..................................................................................................27 5.1.2. Los objetos de juego y los componentes................................................28 5.1.2. La programación..........................................................................................30 5.1.2. La ejecución..................................................................................................31 5.2. El editor.................................................................................................................34 5.2.1. Ventanas básicas.........................................................................................34 5.2.2. Utilidades avanzadas...................................................................................36 5.3. Sistemas de interfaces.......................................................................................37 5.3.1. Interfaces originales....................................................................................37 5.3.2. Textos y texturas en pantalla.....................................................................39 5.3.3. NGUI: Next-Gen UI kit...................................................................................41 5.3.4. La nueva interfaz de usuario......................................................................44 5.3.5. Elección de un sistema para el proyecto.................................................47 6. Desarrollo del creador de HUDs modulares............................................................49 6.1 Diseño.....................................................................................................................49 6.1.1. Las ventanas.................................................................................................50 6.1.2. Los elementos prediseñados.....................................................................52 6.1.3. El funcionamiento........................................................................................56 6.2. Implementación...................................................................................................58 6.2.1. Scripting para el Editor................................................................................59 6.2.2. Prefabs de los elementos de interfaz.......................................................64 6.2.3. Clases y utilización.......................................................................................67 7
Extensión de Unity para la creación de HUDs modulares 7. Desarrollo del videojuego............................................................................................73 7.1. Diseño....................................................................................................................73 7.1.1. Mecánicas.....................................................................................................73 7.1.2. Interfaces en la partida................................................................................76 7.2. Implementación...................................................................................................77 7.2.1. El jugador.......................................................................................................77 7.2.2. Los enemigos...............................................................................................79 7.2.3. La partida.......................................................................................................80 7.2.4. Las interfaces...............................................................................................85 7.3. Estética..................................................................................................................87 8. Conclusión.....................................................................................................................91 8.1. Trabajo futuro......................................................................................................92 9. Referencia bibliográfica...............................................................................................93 10. Anexos..........................................................................................................................96 8
ÍNDICE DE IMÁGENES Imagen 1: Máquina con Computer Space....................................................................15 Imagen 2: Máquina con el juego Pong..........................................................................15 Imagen 3: La HUD tridimensional de Metroid Prime..................................................16 Imagen 4: Edición de blueprints en el editor de Unreal Engine 4.............................19 Imagen 5: Editor de Unity 5 Pro......................................................................................21 Imagen 6: Editor Sandbox de CryEngine 3...................................................................22 Imagen 7: GameObject con gran cantidad de componentes...................................30 Imagen 8: Componente resultante de la implementación de un script..................31 Imagen 9: Ciclo de vida básico de un MonoBehaviour..............................................32 Imagen 10: Vista de las ventanas básicas del editor.................................................34 Imagen 11: Código que renderiza un botón y una etiqueta en pantalla..................37 Imagen 12: Interfaz resultante, antes y después de pulsar el botón.......................38 Imagen 13: GUITexture renderizada en pantalla y opciones del componente......40 Imagen 14: Manipulación directa de un sprite con NGUI..........................................42 Imagen 15: Adición de un sprite a un atlas con NGUI................................................43 Imagen 16: Botones en cuadrícula en un panel deslizante de NGUI.......................43 Imagen 17: Ajuste del rectángulo de un texto manipulando su RectTransform...44 Imagen 18: Manipulación un objeto del espacio como si fuera de intefaz............45 Imagen 19: El sistema de eventos y módulos de interacción por defecto.............46 Imagen 20: Boceto original de la ventana de creación de HUDs.............................50 Imagen 21: Boceto original de la ventana de edición de HUD..................................51 Imagen 22: Diagrama de clases de las entidades del sistema de HUD..................56 Imagen 23: Inicialización de la HUD al iniciar una escena........................................57 Imagen 24: Ciclo de llamadas a la HUD por parte del código del desarrollador...58 Imagen 25: Código que define un selector de color y su resultado.........................60 Imagen 26: Popup mostrado si ya existe una HUD....................................................62 Imagen 27: Ventana de creación de HUD terminada.................................................63 Imagen 28: Ejemplos de structs usados en la ventana de edición de HUD...........63 Imagen 29: Ventana de edición de HUD terminada....................................................64 Imagen 30: Ejemplo de HUDSubtitle.............................................................................65 Imagen 31: Ejemplo de HUDWarningPanel..................................................................66 Imagen 32: Ejemplo de HUDHintPanel.........................................................................66 9
Extensión de Unity para la creación de HUDs modulares los gráficos por computador, que aportan más realismo y variedad a los jugadores con cada año que pasa. Tales cambios aparecen debido al desarrollo de los llamados motores gráficos, herramientas consistentes en conjuntos de programas de renderizado de gráficos en pantalla, modelos de cómputo físico y abanicos de librerías y funcionalidades diversas que permiten la creación de videojuegos completos en sus entornos de desarrollo; además de la innovación en materia de hardware, cada vez más eficiente, potente y accesible. ¿Cómo han, pues, evolucionado hasta el presente los sistemas de interfaces? En nuestros días, podemos disfrutar de una gran cantidad de títulos que poseen elaboradas y flamantes HUDs, con elementos tan variados como barras de salud, medidores de energía, contadores de balas en el cargador de un arma, marcadores de puntos, radares o mapas de la zona donde transcurre la acción y un largo etcétera dependiendo de las necesidades del juego concreto. Tales elementos pueden tener infinidad de formas, acabados y colores según la inventiva del artista tras cada trabajo, llegando a incluso a en algunos casos ser tridimensionales e incluir efectos tales como una oscilación debida al caminar del jugador, o sacudidas cuando se recibe un golpe. Como ejemplo ilustrativo, mencionaremos el sistema de interfaces del título de 2002 Metroid Prime [Imagen 3], de la desarrolladora Retro Studios y distribuido 16 Imagen 3: La HUD tridimensional de Metroid Prime
por Nintendo Company Limited. En ella podemos apreciar una HUD tridimensional con al menos ocho elementos diferentes de interfaz, siendo algunos de ellos bidimensionales pero ubicados en el mismo espacio, simulando estar colocadas en el visor del casco. No olvidando nunca la importancia que tiene la retroalimentación, cabe destacar además que utilizarla produce una grata experiencia al jugador ya que, además de estar sus elementos animados acordemente a la acción, toda la interfaz se tinta de color, oscila y se mueve dependiendo de qué sufra el jugador, séase un impacto, frío extremo o calor intenso. Pero, ¿cuáles son las posibilidades que tiene una persona o equipo que desea desarrollar su juego? Y, en relación con el presente proyecto, ¿de qué facilidades dispone a la hora de implementar un sistema de interfaces como los que hemos comentado? El desarrollador —o equipo de desarrollo— deberá decidir en qué motor de videojuegos basar su proyecto, que podrá ser uno diseñado e implementado por él mismo, o bien alguno de los motores comerciales existentes en el mercado. En la mayoría de casos, el coste del desarrollo y del control de calidad de la primera opción en cuestión de tiempo y dinero invertido resulta inasumible para equipos pequeños o que cuenten con un presupuesto limitado, pero a menudo es la opción elegida por estudios consagrados. Ejemplos de ello son Frostbite del estudio EA Digital Illusions CE, Unreal Engine de Epic Games, Inc., el motor propietario usado por Frozenbyte Inc. para el desarrollo de sus juegos, el aclamado Source de la compañía Valve o la enorme cantidad de motores distintos que Nintendo Company Limited ha creado para un título o serie de títulos en concreto. Por otro lado, existe un abanico de opciones muy asequibles, incluso gratuitas, en lo que a motores gráficos comerciales se refiere. Nos centraremos en estos últimos debido a que son la elección más común para todo aquel que desee dar vida a un juego que haya ideado dadas su facilidad y rapidez de uso, ya que traen consigo numerosas herramientas y utilidades para la creación del juego en su entorno de desarrollo y, por norma general, cuentan con una gran 17
Extensión de Unity para la creación de HUDs modulares cantidad de extensiones o plugins desarrollados por terceros que uno puede adquirir y utilizar para mejorar su experiencia de uso de las herramientas, o bien añadir funcionalidad ya diseñada a su juego. Haremos a continuación un breve análisis comparativo de los motores gráficos comerciales disponibles al público más populares, prestando atención a las facilidades que proporcionan para el desarrollo de sistemas de interfaces en la partida, para después comentar más detalladamente la situación de las interfaces en Unity3D, en el motor utilizado en este proyecto. 3.1. MO T O R E S G R Á F I C O S P O P U L A R E S Cuando nos lanzamos al desarrollo de un juego, son diversas las posibilidades que se nos presentan en el mercado para seleccionar un motor gráfico para nuestro propósito. Destacaremos entre ellos los que responden al nombre de Unreal Engine, Unity3D y CryEngine, por ser las opciones más elegidas hoy en día para la creación de un videojuego tridimensional. 3.2.1. UNREAL ENGINE Creado por la empresa Epic Games, Inc., conocida en aquel entonces como Epic MegaGames, para su exitoso título de 1998 Unreal. A día de hoy se encuentra en su cuarta versión, y desde prácticamente el principio fue muy bien recibido por la comunidad de jugadores y desarrolladores. No solo exhibía un rico abanico de mejoras en materia de estructura, renderizado y rendimiento, sino que además permitía la alteración de los juegos hechos con este motor gracias a la inclusión de un lenguaje de scripting, hecho que atrajo la atención de los llamados modders. Actualmente, este motor es más accesible que nunca, pues desde marzo de 2015 Unreal Engine 4 (abreviado como UE4) y su código fuente están abiertos a todos los usuarios [1]. Presenta aspectos de lo más interesantes como algoritmos de iluminación global altamente eficientes y siempre en proceso de mejora, módulos para el manejo de shaders con multitud de opciones o la programación mediante blueprints, que permite la definir visualmente comportamientos y rutinas mediante sencillos diagramas de flujo para aquellos 18
que no necesiten el absoluto control del proceso. En caso contrario se dispone de programación en el lenguaje C++ para un manejo preciso de la aplicación. Estas características resultan en una calidad gráfica y fluidez de ejecución sobresaliente, además de en una notable sencillez de uso, propiciando así que UE4, además de ser el preferido por muchos para el desarrollo de videojuegos [2], sea la opción elegida para proyectos de renderizado en tiempo real de calidad fotorrealista, como puedan ser muestras interactivas de carácter arquitectónico. El campo de las interfaces no ha sido uno de los puntos fuertes de Unreal Engine, sin embargo. Hasta su tercera versión, para que las interfaces tuvieran un aspecto mínimamente moderno, animado y actual; los desarrolladores normalmente tenían que echar mano de la tecnología Scaleform Gfx [3], actualmente en posesión de la compañía de software Autodesk, Inc., para integrar en sus juegos interfaces basadas en la tecnología Adobe Flash, de Adobe Systems, tanto de propósito general como HUDs. Estas debían ser previamente construidas usando algún software de edición Flash externo, por lo que podemos afirmar que no estaban integradas dentro del entorno de desarrollo de Unreal Engine. El cambio en este ámbito llegó con la llegada de UE4 en el año 2014, a partir del cual los desarrolladores pudieron hacer uso del llamado Unreal Motion Graphics UI Designer, una herramienta para la construcción de interfaces, ahora 19 Imagen 4: Edición de blueprints en el editor de Unreal Engine 4
Extensión de Unity para la creación de HUDs modulares sí integrada en el motor gráfico. Se trata de una suite completa de elementos básicos de interfaz personalizables, por lo que si deseamos crear nuestra HUD con ella tendremos que hacerlo desde cero, como es normal. 3.1.2. UNITY 3D Veamos a continuación qué nos ofrece la alternativa de la desarrolladora Unity Technologies, lanzada en el año 2005. Desde aquel entonces, la compañía ha tratado de mantener la portabilidad entre plataformas de destino como piedra angular de su motor. Tanto es así, que el desarrollador de un juego en Unity no tiene más que pulsar un botón para cambiar entre las plataformas para las cuales compilar su proyecto. El motor reimportará automáticamente los recursos (imágenes, audio, modelos tridimensionales, etc.) del juego con el formato adecuado para la plataforma objetivo y procederá con la compilación, que se hará sobre la API de gráficos utilizada en la plataforma seleccionada, como puedan ser OpenGL, DirectX o Metal. Es este, por tanto, un factor muy a tener en cuenta por aquellos que deseen hacer llegar su producto al máximo número de plataformas posibles. También lo es el hecho que desde que apareciese su segunda versión en el año 2007, el motor ha tenido una licencia gratuita y disponible para todos además de la versión profesional de pago, aumentando notablemente su accesibilidad y, en consecuencia, su presencia en el mercado [4]. Precisamente por estas razones, Unity3D es actualmente el motor elegido por una enorme cantidad de desarrolladores de juegos para móviles, tanto de dos como de tres dimensiones, ya que normalmente buscan desarrollar su juego en poco tiempo y lanzarlo en las tiendas de aplicaciones para móviles como Google Play Store, Apple App Store o Amazon Appstore, sin perder tiempo en el cambio de plataforma ni en desarrollar elementos específicos de cada una. Cabe destacar que, contrariamente a los ofrecidos por motores de la compentencia, el paradigma de desarrollo en Unity suele ser preferido por programadores ya experimentados en programación orientada a objetos puesto que utiliza scripts en lenguajes como C# o Boo para definir el comportamiento de 20
los objetos del juego, y luego esas piezas de código son alterables o utilizables desde el editor del entorno de desarrollo, conocido como Unity Editor [Imagen 5], además de poderse reutilizar para cualquier cantidad de objetos. Por otro lado, siempre ha sido comúnmente aceptado que este motor nunca ha gozado del realismo y calidad a nivel gráfico del que sí gozan sus competidores. No fue hasta la llegada de la quinta y actual versión, Unity 5, que se introdujesen las mejoras que acortarían la distancia entre ellos [5]. Tecnologías punteras como la iluminación global en tiempo real fueron añadidas además de mejoras en la iluminación dinámica de entornos y de largo alcance, en los sistemas de audio y en el manejo de shaders, entre otras. En contraposición, Unity3D ofrece mejores posibilidades para el desarrollo de juegos bidimensionales que Unreal Engine o CryEngine, compitiendo en el sector con motores como Cocos2D o GameMaker: Studio. En materia de interfaces, como detallaremos más adelante, los usuarios de Unity han tenido que valerse de técnicas arcaicas para el diseño de interfaces como el pintado directo de texturas en pantalla, práctica nada flexible, o recurrir a plugins de interfaz de usuario de terceros si querían tener sistemas elaborados. El motor estrenó en 2014 un sistema propio, en el cual centramos este proyecto. Profundizaremos en el tema después de este análisis comparativo porque resulta importante conocer el camino que han seguido las interfaces en 21 Imagen 5: Editor de Unity 5 Pro
Extensión de Unity para la creación de HUDs modulares Unity, dado que es el motor con el que nos disponemos a trabajar. Esta elección ha sido tomada debido a la experiencia obtenida por trabajar con Unity3D en una empresa de desarrollo de videojuegos durante un año. 3.1.3. CR YENGINE Presentamos como tercera alternativa el motor gráfico desarrollado por la alemana Crytek, usado por primera para la creación del videojuego Far Cry en el año 2004. Actualmente está disponible por tarifas desde 9,99 dólares al mes. Ha demostrado poseer desde sus inicios calidad puntera en materia de potencia y rendimiento gráficos, produciéndose con él videojuegos en los que destaca su atractivo visual [6]. Ofrece tecnologías que permiten trabajar con extensísimos terrenos y con grandes cantidades de elementos en ellos, además de disponer de excelentes técnicas de iluminación y sombreado y un motor físico que es capaz de dotar a elementos del entorno de un realismo más que notable. El lenguaje de programación utilizado para desarrollar en CryEngine es LUA. Su editor Sandbox [Imagen 6] incluye herramientas de lo más variadas tanto para la creación de elementos como para el refinamiento visual del juego. Creadores de ríos, caminos, vehículos o editores de animaciones faciales son ejemplos de ello. Este hecho lo sitúa en una posición ventajosa frente a otros motores como Unity, que sí incluye algunos elementos de terreno por defecto 22 Imagen 6: Editor Sandbox de CryEngine 3
pero carece de potentes herramientas de este tipo integradas en el motor, por lo que normalmente se recurre al uso de software externo. En materia de interfaces, no obstante, se encuentra en la misma situación en la cual se encontraba Unreal Engine. Se recurre al uso del anteriormente mencionado Scaleform para integrar interfaces dentro del juego [7], por lo que deben ser creadas externamente en programas de edición Flash. El motor no destaca, por tanto, en este aspecto al no incorporar un sistema de interfaces propio ni una herramienta para trabajar con él. En resumidas cuentas, podemos afirmar que CryEngine es una opción muy recomendable para aquellos cuyo objetivo sea obtener unos resultados visuales detallados e impresionantes en su juego, pese a que el hecho de no ser gratuito suponga un considerable impedimento para según qué bolsillos; pero no es una elección adecuada para trabajar con interfaces en la partida, dado que no dispone de un sistema propio. Se recomendaría, dado el caso, del estudio del funcionamiento de Scaleform. 23
Extensión de Unity para la creación de HUDs modulares 24
4. HE R R A M I E N T A S U T I L I Z A D A S Presentaremos a continuación una breve relación del software utilizado para trabajar en este proyecto, sin entrar en más detalle que el uso principal que le ha sido dado a cada programa. Algunos, como las herramientas de edición de imagen, han tenido un uso puntual o esporádico. •Unity3D: el motor de videojuegos creado por Unity Technologies, elegido para el presente proyecto. Detallaremos más adelante los aspectos más relevantes de este programa. ◦Unity Editor: el entorno de trabajo que ofrece Unity, donde tiene lugar la mayor parte del desarrollo [8]. En él se han construido tanto los elementos de HUD objeto de este proyecto como el juego presentado. ◦Animator: la herramienta incorporada en el Editor para realizar modificaciones en los objetos de juego en tiempo real, dotándolos de animación. Se ha utilizado tanto en los efectos de los elementos de HUD como en los efectos visuales de los enemigos del juego. ◦Mecanim: un controlador de animaciones basado en una máquina de estados, que permite alternar entre clips de animación con fluidez y dinamismo. Con él se han definido los estados y transiciones de los elementos de HUD que pueden ocultarse y mostrarse dinámicamente. •MonoDevelop: un entorno de desarrollo integrado creado por Xamarin y la comunidad Mono [9] para proyectos que usen los marcos de trabajo Mono y .NET, apuntando especialmente al desarrollo multiplataforma. Una versión adaptada de él viene incluida en la descarga de Unity, que es la utilizada en este proyecto para la programación en el lenguaje C#. •Sublime Text 3: un editor de texto plano [10] versátil y lleno de funcionalidades. Se ha utilizado complementariamente a MonoDevelop para cuestiones puntuales. •Git: la herramienta de control de versiones diseñada por Linus Torvalds, 25
Extensión de Unity para la creación de HUDs modulares simplemente definir en su script métodos con el nombre correspondiente al mensaje enviado por Unity, de tal forma que serán llamados llegado el momento. No ocurre absolutamente nada si no se definen, sin embargo. Veamos a continuación una relación de los más conocidos. Comencemos por aquellos que se ejecutan en momentos específicos del ciclo de vida de un componente: •Awake: llamado únicamente cuando el objeto aparece por primera vez en la escena. •Start: llamado después de Awake, sólo una vez y justo antes del primer fotograma en que el objeto está presente en el juego. 32 Imagen 9: Ciclo de vida básico de un MonoBehaviour
•OnDestroy: se ejecuta cuando el objeto va a ser destruido por acción del programa, por un cambio de escena o por el cierre de la aplicación. •OnEnable: se llama cada vez que el componente (o el objeto) son activados en la escena. Si está activado cuando se crea el objeto, las acciones definidas en OnEnable suceden antes que las definidas en Start. •OnDisable: llamado cada vez que el componente es desactivado o justo antes de OnDestroy si fuera a ser destruido. Son muy importantes, también, aquellos mensajes que Unity envía mientras el componente está activo, ya que ofrecen la posibilidad al programador de controlar el comportamiento del objeto y sus componentes en tiempo real. Los más utilizados son: •Update: este método es llamado una vez por fotograma o frame. Muy útil para el control de la aplicación, pero debe utilizarse con cuidado, porque introducir operaciones costosas aquí penalizarán fuertemente el rendimiento general del juego. •LateUpdate: tiene el mismo comportamiento que Update, pero es llamado estrictamente después de los Updates de todos los objetos. Se recomienda su uso para realizar acciones que requieran que cálculos o modificaciones hechas en Update hayan terminado completamente. •FixedUpdate: llamado después de cada paso del bucle de físicas del motor, y es independiente de los fotogramas por segundo a los que funcione la aplicación. Útil para acciones que deban ser independientes en el tiempo del período de los fotogramas, como pueda ser el movimiento de un vehículo. Mencionaremos, finalmente, la existencia de una variedad de mensajes que reciben los componentes por acción de un collider adherido a su mismo GameObject. Estos son: •OnCollisionEnter, OnCollisionStay y OnCollisionExit: son llamados cuando hay contacto con otro cuerpo rígido, en las diferentes etapas del contacto. 33
Extensión de Unity para la creación de HUDs modulares •OnTriggerEnter, OnTriggerStay y OnTriggerExit: similares a los anteriores, pero son llamados cuando otro collider atraviesa uno que está marcado como trigger, tal que no colisiona con nada pero manda estos mensajes cuando hay interacción. Existen otros mensajes de los cuales se puede hacer uso relacionados con, por ejemplo, los pasos de renderizado, pero con los detallados anteriormente es más que suficiente para tener un control eficaz del juego que se desarrolle. 5.2. EL E D I T O R En el llamado Unity Editor es donde tiene lugar la mayor parte de la creación de un juego en Unity3D. En él es donde se construyen las diferentes escenas que formarán parte de él y se administran aspectos varios de la ejecución. Incorpora además un compilador y un entorno de ejecución para los lenguajes de programación compatibles. Cada una de las diferentes ventanas existentes en el editor desempeña una función distinta y todas ellas pueden desplazarse por la pantalla o cambiarse de ubicación. A continuación veremos un conjunto de ventanas y herramientas clave para el uso del entorno de desarrollo. 5.2.1. VENTANAS BÁSICAS Cuando instalamos Unity y abrimos por primera vez el editor, se nos presenta 34 Imagen 10: Vista de las ventanas básicas del editor
una serie de ventanas listas para ser utilizadas. Tales ventanas son las mínimas necesarias para desarrollar con el programa, y son: •Scene: es una representación tridimensional interactiva de la escena de juego actual. ◦En ella podemos manipular a nuestro antojo todos los objetos de juego. ◦Aparecen elementos meramente indicativos llamados gizmos que señalan la posición de distintos elementos, como cámaras o luces. •Game: representa el resultado del renderizado de las cámaras ubicadas en la escena. ◦Muestra cómo luce el juego en cada momento. •Hierarchy: contiene una vista de todos los GameObjects en la escena, agrupados por relaciones de parentesco. ◦Permite copiar, pegar y reorganizar objetos. ◦Permite crear prefabs arrastrando objetos a una carpeta del proyecto. •Inspector: muestra todos los componentes del GameObject seleccionado y permite manipularlos, así como el nombre del objeto o si está activo. •Project: contiene toda la estructura de carpetas que conforman el proyecto y los llamados assets, que es todo aquel material disponible en el proyecto para su uso: prefabs, imágenes, scripts, modelos 3D, sonidos... ◦Premite pasar la referencia de un asset a una variable de un componente simplemente arrastrándola hasta el hueco pertinente del inspector. ◦Permite instanciar copias de un prefab simplemente con arrastrarlos hasta la vista de escena o la jerarquía. •Console: muestra los mensajes del compilador y los generados por los scripts. 35
Extensión de Unity para la creación de HUDs modulares 5.2.2. UTILIDADES AVANZADAS Si bien es cierto que Unity no ofrece tanta cantidad de herramientas para el refinamiento del juego como sus competidores, sí ofrece unas cuantas dignas de mención cuyo uso puede aprenderse en poco tiempo, y pasan a ser imprescindibles en estadios más avanzados del desarrollo. Cada una posee su ventana de editor asociada. Las más destacables son las siguientes: •Animation: permite crear clips de animación de forma intuitiva, consistentes en la alteración de parámetros públicos de los componentes de uno o varios GameObjects a lo largo del tiempo. •Animator: mediante el uso de una máquina de estados, permite transicionar entre las animaciones de un objeto. ◦Si el objeto tiene estructura humanoide, permite controlar cómo sus movimientos se alternan o mezclan. •Lighting: ofrece una serie de ajustes acerca de la iluminación global de la escena [23]. Incluye, además, configuraciones específicas para objetos, así como otros ajustes como la niebla. •Audio Mixer: una ventana con la que agrupar fuentes de sonido, mezclarlas y aplicarles efectos [24]. •Navigation: permite la creación de una malla de navegación, que define por qué áreas pueden moverse los agentes controlados por inteligencia artificial, y ajustar sus parámetros. •Profiler: monitoriza en tiempo real el consumo de recursos que está produciendo el juego en desarrollo, con métricas de muchísimas índoles. Cabe recordar que los desarrolladores disponen de un creciente abanico de utilidades desarrolladas por terceros, que pueden encontrarse en la web o en la tienda Unity Asset Store. Muchas de ellas aportarán nuevas ventanas al editor. La extensión del editor que se desarrolla en este proyecto definirá nuevas ventanas que estarán disponibles para su uso desde el instante en que se añada el plugin al proyecto. 36
5.3. SI S T E M A S D E I N T E R F A C E S Si hace algunos años uno tratara de crear un juego en Unity3D sin ayuda de herramientas externas, se toparía con dificultades que entorpecerían y ralentizarían el desarrollo de sistemas de interfaces en versiones antiguas del motor. Si bien es cierto que sí ha existido soporte pare ello, su uso no era todo lo práctico que cabría desear. Demos a continuación un repaso a las más notables opciones disponibles para la construcción de interfaces y HUDs en este motor. 5.3.1. INTERFACES ORIGINALES Primeramente, centremos nuestra atención en UnityGUI, ahora conocido como Legacy GUI. Se trata de una funcionalidad de Unity3D que, mediante una variada jerarquía de clases, permite superponer a la imagen mostrada en pantalla elementos de interfaz a golpe de código. Con esto nos referimos a que toda la construcción de los sistemas de interfaz con las clases de Legacy GUI a la par que su funcionalidad se realiza mediante scripting, de tal modo que el programador debe utilizar en uno o múltiples ficheros instrucciones que, únicamente cuando el juego esté en ejecución, pinten en cada fotograma o frame los elementos de interfaz definidos en ellas y controlen la respuesta a la interacción del jugador con dichos elementos. Veamos un ejemplo de implementación del método OnGUI [Imagen 11], que 37 Imagen 11: Código que renderiza un botón y una etiqueta en pantalla
Extensión de Unity para la creación de HUDs modulares es llamado por el motor en cada fotograma y es el lugar donde debe definirse toda interfaz que deba visualizarse en pantalla. Utilizaremos el lenguaje C# por ser el elegido para este proyecto. En el fragmento de código presentado, se define un botón estándar, que en los frames en que es pulsado devuelve el valor de verdad true, y en el resto de ellos devuelve false; y a continuación una etiqueta de texto común y corriente. Observemos, además, cómo el programador debe definir también las coordenadas de pantalla (siendo el origen la esquina superior izquierda de la pantalla) y las dimensiones del rectángulo contenedor del elemento. La implementación resulta directa y sencilla, aunque quizá no intuitiva la primera vez que se realiza. Podemos contemplar el resultado, que se muestra a continuación [Imagen 12], ejecutando el juego. ¿Era este el resultado esperado? Probablemente no sea así, dado que en una primera toma de contacto con la librería, cabría esperar que la etiqueta de texto no se superpusiese al botón, dado que se han definido una detrás del otro y por separado. Fijándose, el desarrollador aprenderá que debe definir las coordenadas de los elementos disjuntamente e ir así construyendo su interfaz, o investigar cómo maquetar el contenido usando estructuras más completas; pero en cualquier caso tendrá que volver al código, corregirlo, y volver a probar. Este hecho pone de manifiesto un gran impedimento en la productividad a la hora de trabajar con esta sistema de interfaces: el desarrollador no dispone de retroalimentación instantánea del resultado de lo que está implementando ni puede realizar cambios en caliente que permitan experimentar activamente con los elementos de interfaz que ha ubicado. Cada vez que se desee modificar cualquier cosa, por minúscula que sea, se debe volver al código, buscar la línea adecuada, modificarla, compilar el código y probarlo; lo que induce a incontables retrasos a lo largo del proceso de desarrollo. Esta forma de trabajar dista mucho 38 Imagen 12: Interfaz resultante, antes y después de pulsar el botón
de la usual y conveniente en Unity, que es mediante la administración de objetos de juego o GameObjects y sus componentes directamente en el editor de escenas del enorno. Así pues, destacamos como notable defecto del sistema Legacy GUI el que no disponga de un editor de interfaces propiamente dicho. El problema que este hecho produce se agrava considerando otros factores como que la librería, pese a ser muy completa, resulta muy poco flexible de utilizar, ya que al necesitar definir todos sus atributos a código, el programador tiene que manejar explícitamente asuntos relacionados con el escalado de la interfaz dado que las coordenadas se definen en número de píxeles y, por tanto, son insensibles a las variaciones de relaciones de aspecto y de densidad de píxel que sufrirá el juego dependiendo de la pantalla del dispositivo en el que se ejecute Otra deficiencia apreciable es que al ser meras instrucciones codificadas y no objetos del juego como tal, no son directamente susceptibles de manipularse a posteriori con utilidades que incluye Unity como el gestor de animaciones Animator, que permitiría alterar parámetros de las interfaces en tiempo real y aportaría mucho dinamismo a las mismas. 5.3.2. TEXTOS Y TEXTURAS EN PANTALLA Acompañando al sistema de interfaces basado en scripting que acabamos de ver, Unity3D dispone de un par de componentes determinados, llamados GUIText y GUITexture, que permiten, en esencia, el pintado de texto e imagen bidimensional sobre la pantalla, respectivamente [25] [26]. No obstante, dada su naturaleza, para ser utilizados deben estar agregados a un objeto de la escena de juego o GameObject. Este hecho implica una mejora directa frente a Legacy GUI, ya que permite al desarrollador posicionar y manipular los elementos manualmente en el editor de escena incluso mientras el juego está en ejecución, como podría hacer con cualquier otro objeto y sus componentes. De este modo, el proceso de construcción de interfaces se acelera notablemente, ya que uno puede observar el resultado simultánemante a su trabajo. Los componentes GUIText y GUITexture son mucho más adecuados para la implementación de una HUD, ya que el uso de la transparencia en las texturas y 39
Extensión de Unity para la creación de HUDs modulares la posibilidad de colocación manual de los elementos, que se renderizan directamente sobre la pantalla [Imagen 13], facilitan la obtención de resultados deseables en relativamente poco tiempo. Adicionalmente, estos componentes podrán ser alterados y animados en tiempo real directamente desde el editor o mediante otros scripts que el desarrollador añada a los GameObjects para dotarlos de más funcionalidad, como puedan ser que una textura rote sobre sí misma a lo largo del tiempo o que un texto cambie de color dependiendo de la situación. Se produce así un gran salto en materia de posibilidades de uso, como hemos podido apreciar. Como cabría esperar, sin embargo, los mencionados componentes distan mucho de ser pefectos y no están faltos de inconvenientes. El primero está relacionado con los propios componentes y los valores que el desarrollador puede manipular en el Editor, como puedan ser los de GUITexture, apreciables en la imagen. Un desarrollador sin experiencia en programación gráfica ni conocimientos sobre el funcionamiento de las texturas no comprenderá por qué cuando cambia la pantalla en la cual se renderiza el juego, sus texturas aparecen deformadas siendo que él o ella había ajustado adecuadamente las proporciones y el escalado de las mismas y se visualizaban correctamente mientras desarrollaba. Las consecuencias de la forma de ser y renderizar las texturas, ciertamente 40 Imagen 13: GUITexture renderizada en pantalla y opciones del componente
lógicas pero intrincadas, restan flexibilidad y adaptabilidad a las interfaces desarrolladas de este modo, ya que el programador debe encargarse explícitamente de controlar el escalado y posicionamiento de todos los elementos según la pantalla en uso. Otro muy notable, que podríamos considerar incómodo antes que inconventiente, es que en objetos con GUIText y GUITexture el uso del componente predefinido Transform, que todo objeto posee y determina su posición, rotación y escalado en el espacio, no es coherente con su uso general. Los mencionados componentes requieren que las coordenadas de pantalla donde deberán ubicarse los elementos estén especificadas en el Transform, de tal modo que si los valores en anchura y altura de dicho componente se salen del intervalo [0.0, 1.0], el texto o textura será pintado con centro en el exterior de la pantalla, quedando parcial o totalmente fuera de la visión. No obstante, los valores del componente Transform, siguen definiendo inevitablemente las coordenadas del objeto en el mundo tridimensional, porque en él existen los elementos de interfaz pero después se renderizan totalmente aparte. Este hecho resulta chocante las primeras veces que se utiliza y engorroso todas las demás, y causa la impresión de un pobre trabajo de diseño por parte de Unity. Destacaremos, finalmente, un impedimento para nada ilógico que presenta el uso de GUIText y GUITexture: permiten pintar elementos, pero no al jugador interaccionar con ellos directamente. El desarrollador podrá implementar dicha interacción de diversas maneras, como pueda ser controlar la posición del puntero en pantalla, pero será un trabajo aparte que deberá realizar, ya que la funcionalidad de estos componentes se limita a renderizar elementos en pantalla. Por este motivo, no son directamente viables para la implementación de menús, por ejemplo, pero sí lo son para el desarrollo de HUDs. 5.3.3. NGUI: NEXT-GEN UI KIT Tal es el nombre que recibe la extensión de Unity3D publicada en 2011 por Tasharen Entertainment [27]. Se trata de un extensísimo plugin concebido para 41
Extensión de Unity para la creación de HUDs modulares •Meses de uso de todas los sistemas presentados muestran que este es el menos frustrante. •La curva de aprendizaje es menos pronunciada que la de NGUI y puede competir con él a nivel de características. 48
6. DE S A R R O L L O D E L C R E A D O R D E H U D S M O D U L A R E S Para documentar el proceso de creación de la extensión, bautizada como CustomHUD, expondremos primeramente cómo se ideó y diseñó su apariencia y funcionalidad para luego comentar los puntos más relevantes de su implementación, así como de los objetos de juego y componentes relacionados. 6.1 DI S E Ñ O La sencillez y rapidez de uso son siempre dos factores de mérito a tener en mente cuando se trata de ofrecer un producto al público. Nada más lejos de la realidad, el plugin diseñado apunta a cumplir con su propósito sin suponer esfuerzo alguno al desarrollado a la hora de utilizarlo. Se definirán tres tipos de entidades clave alrededor de los cuales girará toda la implementación posterior. Estas entidades serán: •HUDController: encargado de manejar toda las acciones relacionadas con la HUD que puedan tener lugar. ◦Tendrá un componente Canvas que contendrá los detalles acerca de cómo deben escalar los elementos de la HUD. •HUDAnchor(s): servirán como contenedores bajo los que agrupar los elementos de interfaz en la HUD. ◦Cada HUDAnchor determina a qué parte de la pantalla estarán anclados los elementos. ◦Cada una dispondrá de un componente Canvas independiente que permitirá la ordenación del renderizado. •HUDElement(s): serán las interfaces visibles propiamente dichas, que se ubicarán bajo cualquier ancla. ◦Se dispondrá de una variedad de elementos de utilidad diversa. ◦Se suscribirán al controlador desempeñando un rol específico para que este pueda utilizarlos cuando sea necesario. 49
Extensión de Unity para la creación de HUDs modulares Es importante tener estos tipos de entidades definidos desde un principio, pues se harán menciones a ellos a lo largo de los apartados siguientes. En una escena habrá un único controlador de HUD, y un ancla por cada lugar de la pantalla al que anclar elementos: arriba, abajo, derecha, izquierda y centro. 6.1.1. LAS VENTANAS Para la creación de HUDs modulares, se propone el diseño de dos nuevas ventanas distintas para el UnityEditor, cada una desempeñando una función diferente. Estas son: •Ventana de creación de HUD: presentará una interfaz muy sencilla para que el desarrollador realice, si quiere, ajustes relacionados con el escalado de su interfaz según el tamaño de la pantalla y dote al objeto que representa la HUD de un nombre y de una fuente por defecto para sus elementos. •Ventana de edición de HUD: con una interfaz más completa, permitirá añadir elementos prediseñados a la interfaz, pero con un alto nivel de personalización. ◦Diferentes elementos de interfaz disponibles con las opciones de configuración pertinentes a cada uno. ◦Manejo intuitivo mediante acciones de arrastrar y soltar o entrada 50 Imagen 20: Boceto original de la ventana de creación de HUDs
directa de valores. La función de la primera de las ventana es sencilla. Consistirá en recopilar los datos básicos acerca de cómo desea el desarrollador que sea su HUD y, después, crearla. Instanciará un nuevo GameObject con el nombre elegido y se le añadirá el componente HUDController (que detallaremos en el apartado relativo a la implementación) y un Canvas con los parámetros elegidos. Después se crearán, como hijos del controlador, las cinco HUDAnchor pertinentes. La segunda ventana es más compleja, pues consta de una gran cantidad de opciones. A través de esta ventana, el desarrollador podrá elegir qué tipo de elemento de interfaz desea añadir mediante unos botones ubicados en la parte superior y personalizar diferentes aspectos acerca de ellos a continuación, bien introduciendo datos manualmente o añadiendo material desde las carpetas del proyecto, como puedan ser imágenes, buscándolo o arrastrándolo. Algunos de dichos aspectos serán comunes a todo HUDElement, como los relacionados con el HUDAnchor al que irán anclados y su ubicación respecto a esta. Finalmente, podrá crearse una o más copias del elemento que se estaba personalizando pulsando el botón pertinente al final de la pantalla. Como se puede apreciar en el boceto [Imagen 21], la intención es que la ventana disponga de un espacio flexible habilitado para mostrar un apartado de configuración específica al tipo de elemento de interfaz que se está creando, 51 Imagen 21: Boceto original de la ventana de edición de HUD
Extensión de Unity para la creación de HUDs modulares mientras que el resto, común a todos, se mantiene invariante. Cabe destacar que este boceto fue realizado al principio del diseño y algunos controles más fueron añadidos en la versión final. Enumeremos, a continuación, las opciones de configuración disponibles comunes a todos los elementos de HUD: •Configuración básica: ◦Nombre para el GameObject que se creará ◦Área de la pantalla a la cual anclar el elemento •Configuración del RectTransform del objeto: ◦Distancia al borde de la pantalla ◦Posición/desplazamiento a lo largo de la pantalla ◦Inclinación del objeto •Ajustes de visualización: ◦Si el objeto en cuestión implementa los métodos necesarios para ello, se presentan las siguientes opciones de visualización: ▪Si el elemento de interfaz debe comenzar visible o no ▪Si su entrada o salida de la pantalla deben ser animadas •Efecto de fundido •Efecto de deslizamiento •Tiempo de duración de dichos efectos En cuanto a los ajustes específicos para cada tipo de elemento, veremos cuáles son las opciones de personalización que presenta cada uno en el siguiente apartado cuando se expongan los elementos prediseñados proporcionados junto a la extensión. 6.1.2. LOS ELEMENTOS PREDISEÑADOS Ofrecer una herramienta novedosa para crear HUDs al público resultaría poco 52
más que inútil si no se entregara con elementos de interfaz ya preparados para su uso, por lo que se propone el desarrollo de cuatro tipos diferentes de ellos, cada uno con distinta complejidad y funcionalidad. Veamos cuáles son, qué funciones suelen desempeñar en los juegos y el conjunto de ajustes de personalización para el desarrollador que presentan. En primer lugar, se ofrecerá un elemento para mostrar subtítulos en la HUD. Su uso común consistirá en la transcripción de diálogos o narraciones de voces en off que tengan lugar en el juego, pero también puede ser fácilmente utilizado para traducir a otros idiomas o facilitar la lectura de texto que haya escrito en algún objeto de la escena mientras se mira a él. Acerca de sus características, destacamos: •Puede suscribirse al controlador como subtítulo primario o secundario, por si se quiere alternar en el uso de dos subtítulos con aspecto diferente. •Se le puede dotar de un texto inicial. •Puede dársele una fuente distinta a la por defecto del controlador. •Puede seleccionarse el color del texto. •Se le puede añadir un sombreado y/o un contorno de colores. •Dispone de animaciones de fundido para mostrarlo u ocultarlo. •Se debe anclar exclusivamente al borde superior o inferior de la pantalla, pero se puede distanciar de ellos tanto como se desee. •Su presentación será siempre centrada en la pantalla. ◦El campo de posición es sustituido por el de margen, que indica cuánto margen tendrá el rectángulo de texto con respecto a los bordes laterales de la pantalla. ◦El campo de inclinación es sustituido por el de altura, que indica cómo de alta será la caja de texto y, en consecuencia, qué tamaño tendrá la letra del texto. Como segunda elemento se diseñará un panel de alerta o advertencia, cuyo 53
Extensión de Unity para la creación de HUDs modulares uso más corriente es advertir al jugador de algún hecho de altísima importancia. Normalmente, se trata de un mensaje grande y vistoso ubicado en el centro o la parte superior de la pantalla para que no pueda ser obviado con facilidad. Presentará, como opciones de configuración: •Puede dársele un texto y un icono de advertencia iniciales. •Es posible ponérsele una imagen de fondo al rectángulo de la advertencia. •Se le puede dar color al texto y elegir su fuente. •Puede dársele sombreado y contorno de colores al texto. •Se puede modificar el tamaño del icono o dejar el propio de la imagen. •Puede ajustársele el tamaño del rectángulo. •Se debe elegir si el icono aparece a un lado o a otro del texto. •Dispone de animaciones de fundido al mostrarlo u ocultarlo. Similar al anterior, aunque más versátil y menos vistoso, se proveerá un panel de indicio. Es un recurso muy utilizado en el mundo de los videojuegos cuya función más corriente es indicar al jugador que pulse una tecla o realice una acción determinada según la circunstancia en que se encuentre. Ofrecerá características tales como: •Puede suscribirse como indicación de acción, de recordatorio, u otros (Action, Reminder y Other Hint). •Se le puede dar un texto y una imagen iniciales, así como ajustar su tamaño. •Puede cambiársele la fuente por otra distinta a la por defecto. •Puede cambiársele el color del texto y darle sombreado o borde de color. •Permite posicionar el icono arriba, abajo, a la derecha o a la izquierda del texto. •Dispone de animaciones de fundido y de entrada al ocultarlo o mostrarlo. Finalmente, como cuarto elemento prediseñado se aportará la clásica barra 54
de progreso, utilizada para mostrar cantidades de cualquier índole. En los videojuegos suelen utilizarse para indicar la cantidad de salud, energía o munición restante o el progreso hacia un objetivo, entre otros muchos. Incluirá las siguientes opciones de personalización: •Puede ser suscrita al controlador como barra de salud, energía, experiencia, nivel, u otros. •Se le pueden asignar los valores mínimos y máximos que puede representar. •Puede disponer de un indicador numérico para su valor. •Puede decidirse si la barra utilizará valores enteros o con coma flotante. ◦En el segundo caso, puede definirse el formato de estos números al ser mostrados en el indicador numérico. •Se puede cambiar la fuente y el color del indicador numérico. •Puede añadírsele al indicador un sombreado o borde de color. •Puede elegirse una imagen para el rectángulo de fondo del indicador numérico. ◦Se elija una o ninguna, se puede y debe elegir un tamaño para el espacio que abarca el rectángulo que contiene el indicador numérico. •Puede elegirse una longitud de la barra de progreso y su altura/grosor. •Puede elegirse una imagen para el fondo y otra para el rellenado de la barra de progreso. El desarrollador podrá, una vez ubicados elementos en la HUD, realizar todos los ajustes posteriores que desee modificando directamente los GameObjects que la herramienta habrá creado, o jugando con los parámetros de sus componentes. Podrá también duplicarlos, cambiarlos de un ancla de la pantalla a otra o eliminarlos manipulando la jerarquía de objetos en la ventana de Hierarchy, así como activar, desactivar y añadirles componentes en el Inspector. Además, podrán crearse nuevos elementos de HUD que, si siguen una serie 55
Extensión de Unity para la creación de HUDs modulares de pautas adecuadas, podrán añadirse como nuevas opciones a la ventana de editor de HUD y podrá dotarse al controlador de la funcionalidad necesaria para trabajar con ellos. 6.1.3. EL FUNCIONAMIENTO Dedicaremos este apartado al diseño de la lógica que permite el correcto funcionamiento de los objetos que formarán el sistema de HUD. No se detallará aquí el diseño de la funcionalidad de las ventanas, pues ya se ha comentado cómo es la interacción del usuario con ellas y que su labor es únicamente la de recopilar información introducida por el desarrollador, y crear los objetos acordemente a esos datos. Veamos, por partes, cómo todas las piezas de nuestro sistema encajarán entre sí. Se presenta en diagrama [Imagen 22] la relación existente entre las entidades de nuestro sistema, ahora que las conocemos todas. Servirá como base de la implementación de las clases pertinentes cuando se proceda con la 56 Imagen 22: Diagrama de clases de las entidades del sistema de HUD
programación de la lógica de todo el sistema de HUD que permita la su automatización y uso sencillo y rápido. Pasemos ahora al diseño de cómo interactuarán las entidades durante la ejecución, comenzando por la inicialización. Cuando se disponga de un HUDController y tantos HUDElements como se desee anclados a él y se inicie una escena de juego, la inicialización de la HUD tendrá lugar sin necesidad de que el programador tome parte en ella. Cada entidad por sí misma inicializará lo que necesite en Awake y, cuando un controlador de HUD anuncie que se ha inicializado en la escena, el resto de entidades se suscribirán a él para prestar servicio según el rol con el que se hayan suscrito. Es precisamente este sistema de <<roles>> el que dota a la HUD de su facilidad de uso para el programador y de su modularidad a nivel de funcionalidad. Veamos qué significa. En el apartado anterior hemos enumerado como qué puede suscribirse al controlador cada uno de los elementos prediseñados, salvo para el panel de alerta, que sólo puede cumplir la función de alertar al jugador. El abanico de posibilidades de suscripción de los que dispone cada elemento son los roles que puede desempeñar. Sirven para, por un lado, que el desarrollador defina desde el mismo momento en que crea cada elemento cuál será su cometido; y por otro, para que el controlador de HUD sepa que <<dispone de un elemento cuyo es rol es X>>. 57 Imagen 23: Inicialización de la HUD al iniciar una escena
Extensión de Unity para la creación de HUDs modulares HUDElementCreatorUtility.cs y realiza en su estructura y componentes todos los cambios necesarios para aplicar las preferencias seleccionadas por el desarrollador, resultando en una nueva pieza para la interfaz lista para ser utilizada. El resultado de la implementación de la ventana es el siguiente: 6.2.2. PREFABS DE LOS ELEMENTOS DE INTERFAZ En este apartado nos limitaremos a presentar cómo fueron construidos los cuatro elementos que se proporcionan a modo de prefabs junto a la extensión para ser añadidos a la HUD. Como nota adicional, comentaremos brevemente que se ha utilizado un script para abstraer la carga de dichos prefabs desde el 64 Imagen 29: Ventana de edición de HUD terminada
disco duro. Se trata del fichero HUDResourcesLoaderUtility.cs, que implementa una serie de llamadas de acceso a los recursos del proyecto (Unity provee un rápido acceso desde código a todo elemento ubicado bajo una carpeta que se llame Resources, mediante los métodos Resources.Load y Resources.LoadAll). En la viniente enumeración, se hará referencia a componentes predefinidos de la nueva interfaz de Unity y a unos pocos desarrollados en este proyecto, los cuales fueron presentados en el diagrama de clases de la [Imagen 22] y serán detallados en el apartado siguiente. Además, se presupone la posesión de un componente RectTransform en todos los objetos de juego enumerados, pues es obligatoria su existencia en todo elemento de la nueva interfaz. Los elementos de interfaz y sus estructuras de objetos, en el mismo orden en que se presentaron en el apartado 6.1.2, son: •Subtítulos: un GameObject con el componente HUDSubtitle y, como hijo: ◦Un objeto con un componente Text, que permite el renderizado de un texto en una caja. ▪A este Text se le añadirán los componentes Shadow y Outline si el desarrollador así lo ha querido. •Panel de alerta: objeto con el componente HUDWarningPanel, y: ◦Un objeto con el componente Image para renderizar la imagen de fondo del panel de alerta y el componente HorizontalLayoutGroup, que permitirá mantener a los hijos de este siempre organizados y espaciados. Tiene como hijos dos objetos: ▪Uno con el componente Image para el icono de la advertencia y 65 Imagen 30: Ejemplo de HUDSubtitle
Extensión de Unity para la creación de HUDs modulares ▪Otro con el componente Text para el mensaje. •Panel de indicio: similar al anterior, pero con el componente HUDHintPanel y con un compontente Animator que maneja las animaciones de entrada y salida y, como hijos tiene: ◦Un HorizontalOrVerticalLayout, que ordena los dos objetos bajo él: ▪Uno con una Image para el icono del indicio. ▪Y otro GameObject con un componente Text para el mensaje de ayuda o indicación. •Barra de progreso: utiliza el componente HUDProgressBar y un Animator para animar sus entradas y salidas. Su estructura de GameObjects es más compleja: 66 Imagen 31: Ejemplo de HUDWarningPanel Imagen 32: Ejemplo de HUDHintPanel
◦Un GameObject con un HorizontalLayoutGroup, que organiza: ▪Un objeto que contendrá el indicador numérico del valor de la barra, con un Canvas que permite superponerlo a otros elementos, incluso a la propia barra, si el desarrollador lo desea. •Bajo él, un GameObject con un Image para mostrar un fondo sobre el cual irá ubicado el indicador numérico, que puede utilizarse o dejarse desactivado. ◦Y como hijo de este, uno con el componente Text que muestre el valor de la barra de progreso. ▪Un segundo objeto con el componente Slider (que permite elegir un valor numérico entre dos límites) con la interacción deshabilitada, tal que su única función sea la de mostrar un valor determinado. En el mismo objeto hay un componente Image para representar el fondo de la barra. •Como hijo tiene un objeto con un componente Image, que representará la porción de barra llena. Se expande sobre la imagen de fondo a lo largo de ella, creando así la sensación de rellenado. Tales son, pues, las estructuras que presentan los cuatro GameObjects que se adjuntan a la extensión, en la carpeta Resources/HUDPrefabs. 6.2.3. CLASES Y UTILIZACIÓN Hasta este punto, hemos nombrado algunos de los scripts o componentes 67 Imagen 33: Ejemplo de HUDProgressBar
Extensión de Unity para la creación de HUDs modulares que han sido desarrollados dentro de la librería de clases del proyecto. Ahora es el momento de revisar los aspectos y patrones utilizados más relevantes de las clases implementadas bajo el espacio de nombres CustomHUD. En primer lugar, el uso de las interfaces de C# ha estado presente durante todo el desarrollo. Las interfaces definen grupos de métodos y propiedades que otras clases deberán implementar, estableciendo así un contrato tal que <<si la clase A implementa la interface B, A se compromete a disponer implementados todos los métodos y propiedades definidos en B>>. Gracias a ello, se han podido crear unas férreas normas alrededor de las entidades de la HUD, con el fin de que si cualquier otro desarrollador extiende la funcionalidad de nuestra extensión, lo haga acorde a unas normas y cumpla unos mínimos. En el fichero CustomHUDInterfaces.cs se encuentra la definción de todas las utilizadas. En ellas, se especifican las características mínimas que, por ejemplo, todo buen controlador de HUD debería poseer, o las propiedades que todo panel de alerta o toda barra de progreso deberían presentar. Merece mención especial la interface IHUDShowable y las que heredan de ella, ya que permiten al controlador de HUD saber si un elemento dispone de métodos propios para ser mostrado y ocultado, o simplemente debe activar y desactivar su GameObject para tal propósito. Otro de los patrones popuplares que se ha utilizado es el de llamado singleton débil. Un singleton es aquella instancia de una clase, tal que ella es la única existente de su clase. Se implementa comúnmente creando una variable pública estática del mismo tipo que la clase en sí, tal que sea asignada a la instancia singleton. De este modo la instancia singleton es accesible desde fuera de la clase como cualquier otra variable de clase. En el caso de Unity, lo mismo puede hacerse en un MonoBehaviour. Queremos que sólo exista un objeto controlador de HUD en la escena, por lo que tendremos una variable estática de tipo HUDController en su misma clase, y en el Awake del componente, él mismo se asignará a la variable. De este modo, el desarrollador puede acceder a la funcionalidad del HUDController desde cualquier punto de su código accediendo a dicha variable de clase. 68
El tercer patrón de desarrollo utilizado a destacar es en sí la forma de trabajar con las referencias en los componentes de Unity: las variables de instancia declaradas como públicas serán visibles y manipulables en el editor. Este hecho ha sido de gran utilidad a la hora de construir los prefabs de los elementos de HUD, pues el hecho de poder arrastrar un objeto de la jerarquía hasta el hueco pertinente y que quede asignado a una variable (si la variable es del tipo de un componente, al arrastrar el objeto queda asignada a ese componente, no a todo el objeto) ahorra búsquedas durante la inicialización. Como ejemplo, presentamos la asignación de los Image y Text de un HUDHintPanel, en objetos distintos a él [Imagen 34]. Otras utilidades de las que se ha hecho uso puntualmente son las coroutines, que son métodos que permiten la suspensión de su ejecución durante un tiempo, o los Invoke para retrasar en el tiempo la llamada a un método determinado. Fueron de ayuda para implementar funcionalidades del tipo “mostrar un elemento de HUD y ocultarlo pasados unos segundos”. Por lo que respecta al resto de la implementación de los componentes, se reduce a programación orientada a objetos común y corriente, de forma jerárquica y estructurada. Se creó la clase abstracta HUDElement, de la cual heredan HUDSubtitle, HUDWarningPanel, HUDHintPanel y HUDProgressBar, según se diseñó. También se dispone de las clases HUDAnchor y HUDController, todas ellas resultando en componentes de Unity. La última de las clases mencionadas, HUDController, merece especial atención, pues es la que ata todos los cabos y dota a la HUD de funcionalidad. De ella, destacaremos el uso de un diccionario clave-valor para gestionar el sistema de la suscripción de los elementos según roles y para administrar las llamadas recibidas por parte del código del desarrollador. El diccionario utilizará como claves cadenas de texto o strings, y los valores asociados a ellas serán objetos de tipo HUDElement. Esta decisión fue tomada 69 Imagen 34: Componentes asignados a un HUDHintPanel
Extensión de Unity para la creación de HUDs modulares debido a que dado que a un contolador sólo puede suscribírsele un único elemento de interfaz desempeñando un rol determinado y dichos roles están representados por valores de tipos enumerados (o por el tipo en sí, en el caso del panel de alerta), la representación como cadena de texto provista por el sistema para tales tipos resulta válida como única representación posible de un rol. Así pues, cuando un controlador anuncia que ha sido inicializado en la escena y los elementos de HUD se le suscriben [ref. Imagen 23], los introduce en su diccionario de elementos suscritos, bajo la clave que representa cuál es su rol. Así, en todo momento sabe si dispone de una barra suscrita como barra de energía o de un panel de alerta. Todas los métodos de los que dispone el HUDController relacionados con mostrar, ocultar o manipular elementos de forma externa, utilizan este sistema de roles. El programador podrá llamar a métodos como ShowElement, HideElement o ShowAndHideElement utilizando la clave que representa al rol del elemento, sin necesidad de tener una referencia al objeto en sí. Todavía hay más, sin embargo. Se han implementado varias decenas de métodos públicos que hagan este trabajo tan intuitivo como sea posible tal que, por ejemplo, si el programador desea que se muestre un panel de indicio con un nuevo mensaje, dispondrá de una llamada ShowHint a la que pasarle como argumento el tipo (rol) de Hint al que se refiere y el mensaje que quiera mostrar. Para más comodidad si cabe, se han desdoblado estas llamadas tantas veces como roles hay, para así disponer de métodos con nombres aún más útiles: ShowActionHint, HideReminderHint... Y lo mismo ocurre para el resto de tipos de elementos: existen métodos tal que ShowPrimarySubtitle, SetEnergyBarValue, SetHealthVarBalueOverTime, ShowAndHideWarning, y un largo etcétera. Ante una invocación de entre cualquiera de estas, el HUDController sólo tiene que buscar en su diccionario si existe un elemento suscrito con el rol por el que el programador está preguntando y, si existe, ejecutar las órdenes sobre él que se le indican [ref. Imagen 24]. Concluye así el resumen general del funcionamiento del sistema de HUD. 70
Como comentarios adicionales, merece destacar la existencia de una clase estática llamada simplemente HUD dentro del fichero HUDCallsAPI.cs, cuyo único propósito es contener llamadas estáticas para redirigirlas al HUDController de la escena, tal que utilizar por ejemplo HUD.ShowWarning en cualquier punto del código tenga el resultado esperado. También es digno de mención el archivo HUDEvents.cs, donde se encuentran todos los eventos que el controlador lanza. El desarrollador puede suscribirse a cualquiera de ellos si lo requiere, o puede extender el abanico de los existentes para dotarlo de nuevas funcionalidades específicas. 71
Extensión de Unity para la creación de HUDs modulares 72
7. DE S A R R O L L O D E L V I D E O J U E G O Como parte final del trabajo de este proyecto, se desarrollará un juego acorde con lo propuesto en los objetivos del proyecto [Apartado 2.3]. Será creado desde cero y enteramente en Unity. Aunque el objetivo de su desarrollo sea la demostración del uso de una HUD modular en él, creada con la extensión que se ha implementado, se pretenderá crear un videojuego completo hábil de ser presentado al público. Primeramente, en una etapa de diseño del juego, se expondrán los aspectos más relevantes acerca de su jugabilidad y la interfaz de usuario que se planea construir. Después se darán detalles de su implementación, centrando la atención especialmente en los patrones seguidos y prácticas más comunes del desarrollo en Unity utilizadas. En referencia a la estética del juego, se dedicará a ella un breve punto al final de este apartado, ya que es un aspecto más bien relacionado con el resultado final y presentación del videojuego que no con su desarrollo. 7.1. DI S E Ñ O El proceso de diseño de un videojuego comprende numerosos aspectos, desde su guionización, la toma de decisiones acerca de sus mecánicas, su estilo artístico, el desarrollo de los personajes, etcétera. Dado que nos hallamos ante un juego realmente sencillo de género arcade, la mayor parte de la atención recaerá en sus mecánicas: cómo van a desenvolverse los niveles del juego y los elementos que hay en ellos, cúales son los objetivos a cumplir y cuáles son las acciones que el jugador puede realizar. Además, se hará un hueco para detallar el diseño de las interfaces en la partida que se van a utilizar. 7.1.1. MECÁNICAS Como hemos dicho, se tratará de un sencillo juego de disparos tridimensional. El jugador podrá moverse a lo largo y ancho de una superfície limitada, seguido por una cámara aérea. Además, podrá girar sobre sí mismo 73
Extensión de Unity para la creación de HUDs modulares que tanto los enemigos como el jugador se hayan transladado en Update, en LateUpdate cada enemigo actualiza un Transform auxiliar del que dispone mediante el método LookAt, que lo orienta hacia el jugador. Será en la dirección resultante de este vector en la cual se desplace en el siguiente Update. Para el Enforcer Enemy, se ha creado un cubo tal que cuatro veces mayor a los otros agentes, rodeado por tres anillos circulares. Presenta un anillo a su alrededor formado por partículas que marca el área donde detecta la presencia del jugador. Dispone, además de otros tres sistemas de partículas que cumplen las mismas funciones que los del Basic Enemy. Su movimiento es mucho más lento que el del resto, pero es imposible detener su avance. Cuando el jugador entra en su área de acción, el aspecto de las partículas del anillo cambia a uno más agresivo, para después emitir una onda expansiva que daña y empuja al jugador, acompañado de una oleada esférica de partículas. El empuje se ha implementado con el método AddExplosionForce sobre el Rigidbody del jugador, con centro en la posición del enemigo. Los enemigos también disponen de efectos de sonido, normalmente reproducidos al recibir un golpe o ser derrotados mediante el método PlayOneShot. 7.2.3. LA PARTIDA La situación de la escena y lo que ocurre en ella es manejado en todo momento por el objeto SceneController, cuyos componentes (scripts de C#) 80 Imagen 40: Estructura de un Enforcer Enemy y un Basic Enemy
desempeñan funciones clave para el desarrollo de la partida. Estos son: •SceneController.cs: controla todas las partes de la ejecución y administra la lógica del juego. ◦Carga al inicio, de dos archivos XML, la información referente a cómo serán las oleadas y cuáles son los pasos del tutorial. ◦Muestra el tutorial o el menú principal dependiendo de si el usuario ya ha jugado al juego anteriormente. ◦Tiene una referencia al jugador y le permite o denega el control dependiendo de la situación. ◦Dispone de llamadas para ubicar en la escena enemigos. ▪Permite pasar un método como callback cuando los enemigos sean derrotados (un callback es una llamada a procedimiento dada para que se llame en un momento futuro). ◦Controla la lógica de sustracción de salud a los agentes del juego cuando haya colisiones entre ellos, contra proyectiles o por efecto de una onda expanisva. ▪Se encarga de llamar a las funciones pertinentes de los agentes para que realicen las acciones debidas cuando toman daño o mueren. ◦Administra si la cámara del jugador debe seguirle o debe ubicarse en un punto fijo, así como de colocarla si fuera necesario. ◦Maneja el cierre y reinicio de la aplicación. •LevelController.cs: contiene todo el ciclo de niveles u oleadas de la partida. ◦Contiene una lista de LevelItems (clase definida en LevelItem.cs), construida a partir del XML donde se definen las oleadas. ◦Se encarga de pedir cuántos enemigos de cada tipo deben lanzarse contra el jugador, con un tiempo aleatorio de aparición para cada uno. 81
Extensión de Unity para la creación de HUDs modulares ▪Utiliza una estructura de datos para llevar el recuento de cuántos de estos enemigos quedan vivos. ▪Decrementa estas cantidades según recibe los callbacks al ser derrotados. ▪Cuando ya no queda ninguno, se pasa a la siguiente oleada. •TutorialController.cs: controla el paso del jugador por todos las etapas del tutorial. ◦Utiliza una lista de TutorialSteps, resultado de la lectura del XML al inicio de la aplicación. ◦Muestrea las acciones del jugador con la clase Input para cerciorarse de que cumple los objetivos (moverse, girar y disparar) antes de pasar a la siguiente etapa. ◦Utiliza un subtítulo para comunicarse con el jugador y un panel de indicio para mostrar cómo ejecutar la acción que se le pide. Un punto interesante de la implementación del juego, que anticipábamos en el apartado 7.2.1, es el uso del patrón de desarrollo de las pools de objetos. Podría entenderse como una reserva de, en el caso de Unity, GameObjects de los cuales se van a requerir muchas instancias iguales a lo largo de la partida. La instanciación y destrucción de objetos de forma constante durante la ejecución tiene una sobrecarga notable, pues requiere de un alojamiento dinámico en memoria y de una limpieza de objetos sin referencia constante, generando normalmente fragmentación interna de la memoria y, por lo general, una caída del rendimiento. Para nuestro juego, se ha optado por una pool de objetos de implementación más que sencilla para administrar todos los objetos que deban ser reciclables. Se dispondrá de un número determinado de copias de ellos ya preparados en la escena y desactivados, como hijos de un GameObject que administre la pool. Este administrador se encargará de activar y preparar los objetos según le sean encargados. Cuando un objeto ha cumplido su cometido, se <<recicla>> 82
volviendo a estar disponible en la pool. En nuestro caso, el reciclaje pasará por sencillamente desactivar el objeto en la escena. El impacto en el rendimiento de activar y desactivar unos mismos GameObjects constantemente es despreciable en comparación al de instanciarlos y destruirlos. El funcionamiento es realmente simple: los objetos que deban ser lanzados y reciclados dispondrán de unos métodos para ellos, que deberán ser llamados cuando sea pertinente para asegurar el bueno uso de los recursos. Se han definido dos pools abstractas y objetos para ellas igualmente abstractos, que luego se especializarán en tantas clases que hereden de ellas como haga falta. La primera de ellas es para los proyectiles. Se definen las clases abstractas: •ProjectilePool: rellena una lista de proyectiles a partir de todos los componentes Projectile que tengan sus GameObjects hijos, y la administra. ◦Tiene el método abstracto InitProjectile, que se encargará de preparar un proyectil en la ubicación de un Transform dado. ◦Con el método RecycleProjectile ordena la desactivación de un proyectil dado. •Projectile: contiene una referencia a su pool y los cuatro métodos básicos para su administración, que son: ◦Ready, que lo ubica y orienta en la posición y sentido de un Transform. ◦Launch, que lo activa en la escena y le activa el movimiento. ◦Stop, que lo detiene y desactiva (normalmente ordenado por la pool). ◦RecycleMe, que notifica a la pool que ha cumplido su función y debe ser reciclado. Dado el caso de que una pool de proyectiles tuviese que lanzar uno que estuviera en ese instante activo en la escena (que no haya sido reciclado), simplemente llamaría a Ready y Launch como si nada pasase, ubicando el proyectil donde deba ser disparado y continuando sin problema alguno. En 83
Extensión de Unity para la creación de HUDs modulares relación con este aspecto, se debe jugar con cuántas copias tener instanciadas, para asegurarse que son las necesarias pero no excesivas. La segunda de las pools es para enemigos, y su funcionamiento es prácticamente idéntico al de la anterior. Se compone de: •EnemyPool, con los métodos InitEnemy y StopEnemy. ◦Contiene además el método PlayerHasDied, para notificar a las unidades enemigas que administra que dejen de perseguirlo porque ha muerto. •Enemy, que tiene los métodos Ready, Spawn, Stop y RecycleMe. Cabe comentar, además, que en las pools se han definido algunos atributos propios de los objetos que contienen y no de ellas mismas, pero al ser comunes a todos los elementos en ella y estos tienen una referencia a su pool, se ha preferido centralizarlos en un solo sitio con el fin de evitar redundancias innecesarias. Accesoriamente, para mayor comodidad y mejor organización, se ha creado un componente PoolController, que es añadido al GameObject padre de todas las pools del juego y mantiene referencias a todas ellas, que son accesibles fácilmente gracias al uso del patrón singleton en PoolController. De este modo, por ejemplo, el controlador de escena tiene un acceso fácil a los enemigos para ordenar que se ubiquen en el mapa. Las especializaciones de las clases abstractas que se han implementado para el juego puede observarse en detalle en el diagrama de clases presentado [Imagen 41], que resume la estructuración del conjunto de los scripts desarrollados para el videojuego. Se dispone de la clase estática PrefController, que sencillamente aporta una forma rápida de acceder a las preferencias persistentes del jugador proporcionadas por la clase de Unity PlayerPrefs, y del componente MenuController, que se encargará de la administración de los objetos de interfaz que componen el menú principal del juego. 84
Como comentario final de este apartado y complemento al diagrama [Imagen 41], cabe destacar que en las estas clases existen atributos públicos no reflejados en el diagrama, principalmente porque son simplemente utilizados para pasar referencias a GameObjects o componentes de la escena, y o bien no tienen impacto alguno en el diseño lógico del juego o son inferibles desde las relaciones marcadas. 7.2.4. LAS INTERFACES Para terminar el juego, se añadirá un nuevo HUDController a la escena, y a él 85 Imagen 41: Diagarma de clases seguido en el desarrollo del videojuego
Extensión de Unity para la creación de HUDs modulares se anclarán los elementos de interfaz comentados en el apartado 7.1.2. No hay necesidad de entrar en demasiados detalles acerca de cómo se dispusieron, pues la ventana de edición de HUD lo hace todo por nosotros. Sólo ha sido necesario seleccionar los spritesy fuentes adecuados para cada uno. La selección de tintes de color para algunos elementos se hará a posteriori, cuando se decida una estética general para el juego. Presentamos primeramente una visión general del resultado con los elementos ya dispuestos [Imagen 42]. No presentaremos imágenes separadas de cada elemento, pues en las imágenes [Imagen 30], [Imagen 31], [Imagen 32] y [Imagen 33] ya se muestran, a modo de ejemplo, las versiones primitivas de ellos. Para dotarla de funcionalidad, sencillamente ubicaremos las siguientes líneas a, que llaman a métodos de la clase estática HUD en los lugares pertinentes: •Para la barra de progreso (suscrita como barra de salud): ◦HUD.ShowHealthBar en el método StartLevels de SceneController.cs, que hace la barra visible al iniciar las oleadas. ◦HUD.SetHealthBarValueOverTime tanto en EnemyCollisionPlayer como en EnforcerBurstPlayer de SceneController.cs, para decrementar la barra de vida progresivamente hasta un valor deterimado cuando el jugador ha sido dañado. ◦HUD.HideHealthBar en el método PlayerHasDied del mismo script, 86 Imagen 42: HUD del juego en la vista de escena
cuando la partida ha terminado. ◦La barra dispone de animaciones de fundido cuando se muestra y se oculta. •Para el panel de alerta, sencillamente usaremos: ◦HUD.HideAndShowWarning con la frase necesaria. La frase será el nombre de la oleada en el método CoroutineSetUpLevel del fichero LevelControler.cs y un mensaje de victoria en el método CheckLevelBeaten del mismo script, para cuando el jugador haya superado todas las oleadas. ◦Utiliza animaciones de fundido al ser mostrada u ocultada. •El panel de indicio (como indicio de acción) será utilizado en los pasos del tutorial, en el script TutorialController: ◦HUD.ShowActionHint con el texto de ayuda y el sprite del botón a pulsar para indicar al jugador que realice una acción. ◦HUD.HideActionHint cuando la ha realizado satisfactoriamente. ◦Su entrada y salida están animadas con deslizamientos desde el borde de la pantalla. •El subtítulo (como subtítulo primario) se utiliza también en cada paso del tutorial, para comunicarse con el jugador a la vez que la voz en off: ◦HUD.ShowAndHidePrimarySubtitle con la frase a presentar y el tiempo de estancia en pantalla, normalmente de un segundo. ◦También tiene su entrada y salida animadas con fundidos. El funcionamiento de estas llamadas es el esperado, siempre y cuando el controlador de la HUD esté activo en la escena. 7.3. ES T É T I C A En este último punto, veremos algunos detalles acerca del apartado visual del videojuego, como puedan ser el conjunto de colores elegido, el diseño final de los 87
Extensión de Unity para la creación de HUDs modulares enemigos y sus animaciones o los efectos de cámara. Se ha optado por un juego de colores que oscila entre el morado y el verde para todo el entorno e interfaces. Para el jugador y sus proyectiles, colores más fuertes pero en sintonía con ellos, hacia el fucsia. Por contra, para los enemigos se ha elegido una gama de naranjas que contrasten con el entorno. Para los elementos de HUD se ha utilizado la familia de fuentes Roboto, y para el panel de alerta, la fuente Marquee Moon [Imagen 42]. Para el menú principal, en cambio, se ha utilizado la fuente Planet Opti [Imagen 43]. 88 Imagen 43: Menú principal del juego Imagen 44: Apariencia de la HUD dentro del juego
El jugador va perdiendo la opacidad de su color según decrece su vida y emite un sonido al ser vencido. Las dos cintas, producto del componente TrailRenderer de Unity, siguen su movimiento en todo instante. Los proyectiles, por su lado, no son más que un cubo deformado a lo largo que rota sobre sí mismo y emite una partícula en su cola que cae con la gravedad [Imagen 45]. Una serie de animaciones para los enemigos ha sido desarrollada para cuando aparecen, son golpeados o son derrotados [Imagen 46], que incluyen desde el uso de sistemas de partículas hasta la alteración de sus dimensiones o posición simulando un crecimiento o un empequeñecimiento; y suelen ir acompañadas de sonidos. Además, su color va cambiando según son dañados [Imagen 47]. Finalmente, para mejorar la ambientación del juego, se ha recurrido al uso de algunos efectos de cámara que Unity proporciona como componentes dentro del 89 Imagen 45: Jugador en movimiento disparando a un enemigo Imagen 46: Enemigo básico en movimiento, siendo golpeado y derrotado Imagen 47: Enemigo Enforcer atacando y siendo derrotado
Extensión de Unity para la creación de HUDs modulares 10. AN E X O S Se proporciona el siguiente hipervínculo al repositorio git alojado en Bitbucket que contiene todo el material utilizado y desarrollado a lo largo de todo el proyecto: bitbucket.org/a_diaz/modular_unity_custom_hud. 96