Infinitamente mejor la última imagen, pero aun así sigue faltando algo de descanso visual.
Añadiría que las sombras deberían ser más light no tan oscuras.
Infinitamente mejor la última imagen, pero aun así sigue faltando algo de descanso visual.
Añadiría que las sombras deberían ser más light no tan oscuras.
#32 Gracias, a mi también me gusta como va evolucionando, aunque haya cambiado bastante de la idea que tenía en un principio.
#31 Lo primero decir que las sombras se pueden desactivar a gusto del jugador en las opciones. Lo había implementado, bien por puro detalle jugable (habrá quien percibiera mejor las profundidades sin las sombras) o bien por rendimiento para los hardwares menos potentes (que no debería ser problema porque el juego está bastante bien optimizado).
Después de hacer un testeo durante media hora con las peores condiciones posibles (habitación con las luces apagadas y el móvil con el brillo al máximo, puedo decir que estás en lo cierto. Aunque muy levemente pero he terminado viendo lucecillas durante unos segundos y las sombras tan oscuras pegaban bien duro.
Para solucionarlo lo primero que he hecho es ajustar el brillo de mi pantalla a modo Dinámico para que se autoajuste en vez de a modo Texto xD. Parece una tontería pero si no tienes bien configurada tu pantalla dificilmente aprecias ésto en el monitor.
Ahora si, en el juego he suavizado un poco más los materiales más suaves (poquito) y he modificado completamente la iluminación global para suavizar las sombras y también igualarlas, ya que anteriormente había unas sombras mucho más intensas que otras. Y ahora, como las imágenes valen más que mil palabras, os dejo la comparativa.
Cambio en el menú
Cambio en nivel
No hay "color" xD entre lo de antes y lo actual.
Ahora es agradable y jugable sin que te de un ataque de epilepsia, buena adaptación.
DEVLOG - GENERACIÓN PROCEDURAL
¡Buenas! En este post os voy a hablar un poco de como está implementada la generación procedural en el juego. Cuando comencé a desarrollar Volumetris tenía muy claro que no quería meterme en trabajos de modelado en 3D para este proyecto. Así que, para simplificar las cosas, me decidí por crear un simple plano en Blender y utilizarlo para crear todo en el juego, escenarios y piezas en niveles y mapas de selección de nivel, al final incluso el menú de inicio.
Saludos del todopoderoso Plano
Los que os pregunteis por qué cree el plano en Blender en lugar de usar el propio de Unity os diré que un plano en Blender tiene dos triángulos mientras que el plano de Unity tiene cientos de ellos.
Lo curioso es que en principio iba a hacerlo todo a mano . Cuando empecé a hacer el primer prototipo de nivel me di cuenta que poner los planos uno al lado del otro puede que fuera cómodo para crear los niveles, pero había algo que no era: rápido. Tardar tanto para crear cada nivel era una sentencia para el proyecto. Como programador os diré que mi mejor manera de definir mi profesión es: "Programar es decir a una máquina lo que tiene que hacer por tí". Así que en ese momento me planteé mi profesión. ¿Por qué hacer el escenario a mano si puedo programar para que lo haga Unity?
GENERACIÓN DEL ESCENARIO
Así pues, sabiendo que quería que el escenario de un nivel se generara solo, me puse manos a la obra. Lo primero es lo más simple, instanciar el plano en las coordenadas correspondientes el número de veces necesario. Tarea simple.
Escenario Plano
Sólo haciendo ésto uno ya se da cuenta de cuánto se ahorra en tiempo si el ordenador hace las cosas por ti. Bueno, ésto era lo fácil, lo siguiente ya era más complicado. Ahora necesitaba que se generase el escenario. Pero claro, no quería que se generase de manera aleatoria, quería que se generase automáticamente, pero con el aspecto que yo quisiera. Aquí llegó la hora de utilizar una tabla que referenciase al escenario y que tendría las profundidades a las que yo quería cada parte del suelo.
Los que conozcais Unity sabreis que la edición de arrays desde el inspector deja mucho que desear. Para facilitar la introducción de datos y para que todo fuera más simple a la hora de trabajar con ello realicé mi primera modificación al inspector de Unity para mostrar la tabla de una manera fácilmente comprensible. Se puede hacer más bonita, pero como solo la voy a utilizar yo, no me preocupa su estética.
Tabla en el inspector para crear el escenario
Teniendo una tabla tan accesible, hacer que el escenario se generase en base a ella ya no sería un trabajo tan engorroso. "Sólo" tenía que modificar la altura de los planos correspondientes para ajustarlos a la profundidad deseada. Después de eso el paso sería comprobar cada plano con respecto a los que tiene alrededor para generar los planos verticales que serían las "paredes".
Escenario Generado en base a los datos configurados en el inspector
Lo más dificil ya estaba hecho, lo último sería simplente darle color a cada plano. Aunque hubo cambios durante el desarrollo tanto en los materiales como en la forma de asignarlos, al final se quedó en un simple: Si está a la altura del suelo le asigno un color aleatorio y punto, si está más profundo lo dejo blanco para diferenciar los huecos que hay que rellenar más claramente.
Escenario final
Al final, como siempre queda mejor ver las cosas que leerlas, aquí una representación muy básica de los pasos realizados.
GENERACIÓN DE LAS PIEZAS
Del mismo modo que conseguí que el escenario se generase solo y además como yo quisiera me planteé lo mismo con las piezas. Podría hacer un numero de piezas determinado y reutilizarlas según necesitara, pero sería mucho mejor si pudiera crearlas como yo quisiera para cada nivel, así tendría piezas únicas siempre que quisiera. Así que eso me propuse.
A diferencia de la generación de escenarios, aquí la pieza básica no iba a ser el plano. Me parecía demasiado engorroso. Así que simplifiqué esta tarea aunque sabía que sacrificaría algo de rendimiento por ello (ya hablaré de rendimiento en otro momento). Lo que hice fue crear un prefab de un cubo con los seis planos que ya tenía y así, sería el prefab del cubo lo que instanciaría.
Seis todopoderosos Planos juntos por el bien común
Al igual que con los escenarios, quería tener el control en todo momento de como sería la forma de cada pieza, por lo que en este caso cree un array de tres dimensiones que almacenase los "huecos" que ocupaba la pieza. Para usarlos en el inspector tuve que volver a retocarlo para hacerlo algo más cómodo.
Configuración en el inspector para crear una pieza
Pues tan simple como que se generase la pieza situando el prefab del cubo en cada uno de los huecos que esa pieza ocupa y, obviamente, no situándolo donde esa pieza no ocupa espacio.
La pieza configurada en el inspector
Y, como antes, el último paso es asignar un material. En el caso de las piezas pensé que lo mejor era que toda la pieza fuera de un único material y no materiales mezclados aleatoriamente como en el suelo. Podía asignar un único material aleatorio pero creí que era mejor tener el control del color de cada pieza. Era tan simple como asignar un material desde el inspector, y asignar ese material a cada plano al generar la pieza.
Pieza final con la forma y color configurados en el inspector
GENERACIÓN DE LOS MAPAS
Lo último que explicaré, ya más levemente, es la generación de los mapas de selección de nivel. Para ellos la base es la misma que en el escenario, un suelo plano fácilmente realizable. Lo único peculiar es la posición de los botones y la generación de los caminos que hay entre ellos. Cuando me planteé ésto lo que pretendía era evitar que, además de hacer los niveles tuviera que crear a mano los botones para cargarlos. Funciona de un modo simple, descarta por donde no puede ir y, entre las opciones que le queden, escoge aleatoriamente. Las distancias mínimas y máximas de diferencia entre niveles también están configuradas para evitar caminos excesivamente largos.
Secuencia de mapas de treinta niveles generados aleatoriamente
Lo mejor de la generación procedural en Volumetris es que me permite tener todas las piezas y escenarios diferentes que quiera sin apenas ocupar espacio en disco (ideal para reducir el tamaño de una build para móviles).
Lo peor es que generar tantos objetos (para que os hagais una idea, solamente en el suelo del menú principal hay 14400 objetos) reduce el rendimiento muchísimo. Tanto que incluso se resiente un PC, como para que no lo note una plataforma móvil.
Pero el caso del rendimiento y optimización da para otro Devlog que espero hacer si tengo tiempo la semana que viene. Así que por ahora os dejo. Ésto es todo lo destacable de la generación procedural en Volumetris.
Buenas a todos. Estoy en los últimos pasos del desarrollo de Volumetris y ahora me toca decidirme por un logo para el juego. Todos sabeis perfectamente que el logo es importantísimo para llamar la atención de los jugadores para que, al menos, se dignen a mirar de que va tu juego. En un juego como éste no es que se pueda ser muy original con el diseño del logo, aun así me gustaría preguntaros ¿que opinais de las siguientes opciones para los logos? ¿Cuál os gusta más?
Personalmente, obviaría logos del estilo 4, 5 y 8, ya que no se refleja la propia idea que puede transmitir el juego, un usuario que vea ese logo no creo que le llame siquiera descargárselo.
Sin embargo, algo del estilo del 1 o el 3 creo que si lo explica bien. Yo creo que tendrías que enfocarlo por ahí.
Por otra parte, tengo ganas de probar el juego, pinta muy curioso
#38 La verdad es que los que nombras (4, 5 y 8) solo los puse por darle un toque diferente de la estética del juego y por probar, aunque obviamente son los peores. A mi me gustaba el 7 pero tienes razón en que el 1 y el 3 representan mejor de que trata el juego y en un juego de este estilo es importante mostrar eso de primeras. Seguramente tire por ahí. Gracias por el comentario.
DEVLOG - OPTIMIZACIÓN
¡Buenas a todos! Ya había comentado que iba a hablar del tema de optimización en un devlog hace algún tiempo. Pensaba hacerlo antes pero el trabajo en Volumetris (que por cierto, publico mañana no me ha dejado tiempo hasta ahora.
La optimización es un aspecto muy importante en todos los videojuegos. Hoy en día hacerse con hardware muy potente no es tan problemático como hace 10 o 20 años, época en la que los desarrolladores trataban de ahorrar hasta el último Kb, por eso en la actualidad muchos desarrolladores le dan menos importancia de la que realmente tiene. Pero optimizar bien el rendimiento de un videojuego nos permite mucho "juego" a la hora de trabajar con el, poniendole más características, haciendolo más complejo, más bonito o, simplemente, permitiendo que mucha más gente pueda disfrutarlo en todo su esplendor.
Dicho este tostón histórico vayamos con lo importante, que es la optimización llevada a cabo en Volumetris para que pudiera disfrutarse cómodamente en nuestros dispositivos móviles, aunque sean de gama baja. Solo voy a hablar de lo realizado en la optimización que más influye en el rendimiento, no de todas las medidas utilizadas para optimizar Volumetris en todos sus aspectos. Lo primero es empezar a ver como rendía Volumetris antes de optimizarlo. Las imágenes son de hace tiempo, por eso tienen la estética anterior del juego.
Aviso: el rendimiento reflejado aquí es en base a mi PC, no a un móvil, en cualquier caso, por las pruebas realizadas, me atrevo a decir que las mejoras del rendimiento se notan incluso más en los dispositivos móviles de lo que se notan en un PC cualquiera.
Datos del rendimiento en el menú principal antes de optimizar.
En esta época es cuando probé el juego en móvil por primera vez. A la hora de interactuar con él me daba cuenta de que ya solo en los mapas de selección de niveles iba a tirones, pero es que simplemente en el menu (lo que corresponde a las imágenes en las que se ve esta comparativa) el pulsar el botón de jugar tenía un retardo muy grande.
Ya cuando creé el juego de manera procedural me imaginé que crear tantos objetos no iba a ser positivo para el rendimiento y es que, en el menu que se muestra en estas imágenes, solo en los planos que forman el suelo hay ya 14400 objetos. Cada uno con su correspondiente mesh, material y collider además de las sombras que generan, lo que hace entender el porqué de ese problema de rendimiento.
De todos modos esos 42.4 fps que me daba me resultaban chocantes pues, si bien no tengo un pc de la NASA tampoco es tan malo y puedo jugar a juegos mucho más potentes como Skyrim, Deus Ex: HR a 60 fps o más con todo al máximo. Se que no son los ejemplos más potentes del mundo, pero si son mucho más potentes que Volumetris. El caso era, ¿porque un videojuego que tiene tan pocos polígonos puede resultar tan problemático a la hora de rendimiento cuando, sin ir más lejos, mi proyecto anterior tenía más polígonos e iba sobrado? Investigando un poco encontré el motivo.
El motor Unity (desconozco si pasa lo mismo con otros, pero es posible que sea igual) trabaja de un modo muy poco eficiente si tiene que manipular meshes reducidas. Utilizando un símil informático sería como la transferencia de ficheros. Si mueves un archivo de 2 gigabytes de tu disco duro a un pendrive será muchísimo más rápido que si mueves 1 millón de archivos de un Kb. Mover muchos archivos pequeños es mucho más lento que mover uno solo grande. Lo mismo pasa con Unity, trabaja mucho más lento con muchas meshes pequeñas que con una sola grande.
Sabiendo ésto ya se sabe lo que hay que hacer, ¿no? Por supuesto, unir todas las meshes en una sola. Tan simple como eso. Pues bien, lo primero es saber como se hace y para eso, en Unity, existe un método de la clase Mesh que se llama CombineMeshes. El caso es que la documentación es escasa así que os voy a comentar como lo hice en mi caso. Lo primero fue crear tantos objetos como materiales utilizaba (en el caso de las imagenes 8) y, al generar cada plano y asignarle un material, lo colocaba como hijo de ese objeto. Así, al final, tendría 8 objetos padre que tendrían cada uno, todos los planos del material correspondiente. Pero así no cambiaba nada, simplemente había agrupado los objetos por su material.
Una vez con todos los objetos con mesh agrupados era hora de unirlos. El procedimiento es el siguiente, creé un mesh vacio para el objeto padre, con ese mesh y el método CombineMeshes fuí juntando todos los meshes de los hijos y le asigné el material correspondiente (el mismo que tendrán todos sus hijos) y, en el caso del menú, eliminé su hijo completamente. Así, lo que me quedaba como resultado es 8 GameObjects, cada uno de ellos con un mesh correspondiente a un material. ¿El resultado?
Datos del rendimiento en el menú principal con el suelo optimizado a 8 meshes.
La mejora fue sustancial pero no suficiente. Sabiendo el modo de hacerlo solo me quedaba seguir optimizando toda la escena. Había optimizado los 14400 meshes del suelo y los había convertido en solo 8. Pero seguía habiendo muchos formando las letras del título. Tocaba el momento de hacer lo mismo con esas letras y unirlas a los 8 padres segun el material que tuvieran. Así lo hice y éste fue el resultado de dicha feliz unión.
Datos del rendimiento en el menú principal con el suelo y las letras optimizados a 8 meshes.
Ahora si la mejora era brutal y evidente, y es que había cambiado una escena en la que había decendas de miles de planos con su respectivo mesh formando el suelo y las letras en una en la que había solo 8 meshes en escena. Como ya dije, a pesar de que estos meshes eran mucho más complejos, el rendimiento mejoraba mucho al haber menos cantidad de ellos.
Podría valer, pero aun se podía mejorar más. ¿Como? Pues fácil, ¿había 8 meshes, no? Pues, ¿porque no convertirlos en uno solo? Este paso fue el más raro por ser el menos documentado, el caso es que combinar meshes que tienen distintos materiales es muy simple pero la documentación al respecto es nula. Lo importante, por experiencia propia, es que si vas a combinar meshes de distintos materiales, TODAS las meshes tengan distintos materiales. No puedes combinar a la vez 5 meshes que tengan 3 materiales diferentes. Si combinas 5 meshes, o todas con el mismo material, o cada una con un material diferente.
Combinado todo, el resultado era éste:
Datos del rendimiento en el menú principal con todo unido en un único mesh.
La mejora es ya mínima con respecto a antes, pero comprensible pues se ha pasado de 8 a 1. No es tan acentuada la diferencia. De todos modos se ha cambiado una escena con decenas de miles de GameObjects por otra en la que solo hay uno. La comparativa final es la siguiente.
A pesar de haberse aumentado el número de polígonos en escena a casi el triple, el rendimiento en todos los aspectos ha mejorado de un modo estratosférico. Como resumen final, los datos tras la optimización cambiaron así.
FPS: se aumentaron un 5221%
CPU main: se redujo el tiempo un 98.3%
Render thread: se redujo el tiempo un 98.45%
Batches: se redujeron un 99.2%
Saved by batching: se redujeron un 99.98%
Tris y Verts: se aumentaron un 176.89%
SetPass calls: se redujeron un 99.17%
Shadow casters: se redujeron un 99.79%
¿Os parece una buena mejora? ¿Creeis que merece la pena optimizar? He visto algunos buenos proyectos en el desafío de generación procedural, a ver si alguno os animais a optimizarlos a ver si notais las diferencias.
Como último añadir, ésto no se puede hacer alegremente sin consecuencias. Al unir las meshes, eliminas toda posibilidad de trabajar con ellas de manera individual, lo que puede suponer un problema. En el caso de esta escena, es un menú y no va a haber ningún tipo de movilidad por lo que es perfecto, también en escenarios de juegos como una ciudad, por ejemplo. Pero hay otras escenas en mi juego que si requieren movilidad independiente (como los botones en la selección de mundos/niveles) que no pueden unirse al resto.
¡Volumetris ha sido publicado hoy! Podeis descargarlo de Google Play aquí: Volumetris.
Os dejo el trailer (simple) que he hecho para el juego:
Y por último el enlace al post de lanzamiento de la sección de juegos móviles.
Lanzamiento de Volumetris
Supongo que este hilo ya se puede cerrar. Gracias a todos los que me habeis ayudado con consejos, en especial a @sodAp y a @Cryoned por su ayuda con la parte estética y a todos los que os habeis interesado en el videojuego. ¡Saludos!