Algunas nociones sencillas de matemáticas en programación.

Las viejas matemáticas simplonas, los problemas que nos dieron en la EGB (a los que ahora somos viejos y caducos) y los problemas que nos resuelven ahora. Por ejemplo, los botones para conmutar estados. Nos gusta reducir al mínimo los condicionales (if … then…) para reducir el código y hacerlo mas legible. Pongamos que tenemos un botón que active o desactive cierto estado, podemos hacerlo así, suponiendo que el estado pueda ser 1 o 0  (pongo código «inventado» para facilitar la comprensión, la sintaxis de javascript en unity es muy distinta)
if pulsarboton {
if (estado=1) then (estado=0) else (estado=1)
}

Pero recordemos que según las matemáticas esas, positivo * positivo=positivo, positivo*negativo= negativo y negativo * negativo= positivo, así que podemos simplificar el código si dejamos los posibles estados como 1 y -1 en vez de cero, y al pulsar el botón se multiplica por -1, quedaría de esta manera:
if pulsarboton {
estado= estado *-1
}

Y se conmuta el estado automáticamente de forma un poco más sencilla y sin bucles «if». Otro truco para evitar condicionales es usando la propiedad de que  cualquier numero multiplicado por cero es igual a cero. Pongamos que tenemos una variable x que se incremente o decremente al pulsar los botones de izquierda-derecha para menear un personaje, podemos hacerlo así.
if pulsarizq=1 {
x=x-5
}
if pulsarder=1{
x=x+5
}

Pero podemos simplificarlo así en una sola linea sin «ifs»:
x=x+(5*pulsarder)-(5*pulsarizq)

Si uno de los dos valores es 1 se realizara la suma/resta, sino, sumara cero y no se moverá. Si queremos añadir inercia para hacer el movimiento más natural, o simular que el personaje anda sobre el hielo, no variaremos directamente la posición x del personaje, sino que cambiaremos su aceleración con una variable intermedia.
acel=acel+(5*pulsarder)-(5*pulsarizq)
x=x+acel

Si queremos que el personaje frene si no se pulsan los botones, hay que hacer que ac tienda a cero añadiendo algo como lo siguiente:
acel=acel/2

Y ademas hacer que la aceleración se convierta en cero si es muy pequeña
if (acel*acel)<0.4 {
acel=0
}

Usamos el cuadrado de ac para convertir su valor en positivo y evitar dos condicionales, uno si es positivo y otro si es negativo. Con el truco anterior, aun podemos eliminar el if y juntar todo lo anterior haciendo esto:
acel=(acel/2)*((acel*acel)>0.4)+(5*pulsarder)-(5*pulsarizq)

De esta forma, si acel al cuadrado es menor que 0.4, la comparación devuelve cero (false) que al multiplicar por la aceleración anula esta, y hemos incluido también el control y el frenado sin tener que utilizar ni un solo bucle if y en una sola linea. Nuestro programador es un crack. Espero que no se entere la competencia y le llame.
Cambiando los distintos valores, haremos que el personaje acelere o frene mas rápido, pudiendo simular que camina sobre tierra, hielo, manchas de aceite, etc.

Otro caso que nos ha ocurrido recientemente, aunque en otra aplicación, no en este juego, demuestra que mucha veces es mejor gastar el tiempo buscando el camino más sencillo que torturando una solución complicada para que funcione. Es un juego en el que podemos colocar en cualquier posición una muñeca articulada en 2d, una especie de marioneta. Queríamos que al mover la mano, la combinación brazo-antebrazo se moviera de forma natural siguiéndola, lo que se conoce como  cinemática inversa (IK) en animación. Estuvimos probando pluguins y código ajeno, pero eran muy complicados porque estaban preparados para múltiples articulaciones en un escenario 3D, nos rompimos la cabeza intentado simplificar el código para limitar la rotación solo en un eje sin mucho éxito, hasta que nos dimos cuenta que en realidad, estábamos hablando de resolver los ángulos de un triangulo conocidos la longitud de los tres lados (el brazo, el antebrazo y la distancia entre la mano y el hombro), que como todo el mundo sabe (después de buscar en internet)  sigue la siguiente formula:

triangulosAsí (usando la inversa del coseno) sabemos en que angulo tenemos que girar los objetos que forman el brazo y el antebrazo. Con un par de ajustes funciona perfecta y rápidamente  sin usar complicados algoritmos.

Bueno, igual todo esto le sirve a alguien, se puede aplicar a cualquier lenguaje de programación con los debidos retoques, seguimos en contacto, adeu.

Algunos trucos sucios

Vamos a contaros ahora algunos de los sucios trucos que hemos usado con el noble fin de trabajar menos o sortear problemas que nos costaría mucho resolver de forma mas «limpia».
Como comentaba en otro post el explorador tiene velocidad variable, corre más cuando la roca esta cerca. Esto tiene dos objetivos:

-Por una parte, hacerlo más divertido y «realista»
-Por otra, dar más margen y libertad de juego. Supongamos que el explorador tardase de forma fija 30 segundos en recorrer el laberinto, entonces solo tendríamos ese tiempo para resolverlo antes de aparecer el mensaje de fin de partida, y resultaría frustrante, no tendríamos tiempo para acostumbrarnos, sobre todo en fases complicadas y tendria un comportamiento muy rígido.

Hemos hecho que el mangante deje de acelerarse al tener la roca cerca cuando se encuentre a cierta distancia de la salida. Antes de que llegue a ese punto, correrá tanto que será imposible cazarlo.
Así, un jugador experimentado puede acabar la pantalla en los 30 segundos mencionados antes, con el explorador corriendo a toda leche, pero un jugador novato podrá acabarla en (pongamos) un minuto atascándose por el camino, con el explorador corriendo despacio. En la practica funciona como un temporizador con un máximo y un mínimo. Por eso el consejo para pasar las pantallas es sobre todo centrarse en resolver el laberinto sin intentar chafar al tipo antes de tiempo.

Programar es como descabezar una Hidra, cuando  resuelves un problema aparecen 3 nuevos. Por ejemplo, en la fase del molino, el tema de sincronizar el giro de las aspas con la vagoneta para evitar que se golpeen seria facil si la velocidad fuera fija, pero no sabemos cuando va a llegar el personaje a las aspas para ajustar su giro. La forma más sencilla que hemos encontrado es algo parecido al truco de la visión de aquel post, poniendo unos objetos invisibles pegados a las aspas.  En la siguiente imagen los hemos hecho activado para que se vean, los que están pintados en blanco, hacen que el molino acelere al colisionar con la vagoneta, los amarillos, que frene. Así evitamos que choque con ella. Es un truco sucio pero no se nos ha ocurrido otra forma de hacerlo.
detectores

La siguiente jugarreta avergüenza al diseñador pero tendrá que vivir con ello. Durante las fases de las minas, el explorador pasa de ir corriendo a montar en una vagoneta. Le pedimos que hiciera una animación de forma que saltara dentro y se pusiera en marcha y ¿que nos entrego? El personaje se mete en una cueva y vuelve a aparecer ya montado y se ahorró todo el curro de animarlo. Se aprovecha de que no puedo rebajarle el sueldo (no tiene)
cueva

El siguiente truco es sensato y se usa mucho en juegos, utilizar colisionadores sencillos ocultos bajo formas complejas que en realidad no afectan. En nuestro caso, a veces aparecen unos esqueletos de antiguos saqueadores sin fortuna que entorpecen la marcha de la roca. Los huesos en realidad no afectan a la bola, sino unas pirámides que hay ocultas en su misma posición. Esto hace que las físicas sean mas rápidas porque al procesador le cuesta menos calcular las colisiones de la bola con objetos sencillos (como las pirámides) que con objetos complicados como los huesos con sus recovecos y esquinas. En la siguiente foto hemos apagado el esqueleto de la derecha e  iluminado las pirámides para que las veáis. En los juegos, como en la tele, todo es mentira.

esqel

Ale, ya os contaremos más problemas que nos hemos encontrado, por desgracia hay material para muchos posts, hasta luego.

Manejando el caos pantallistico

Cuando hacíamos (y hacemos) webs nos quejábamos (y nos quejamos) mucho del problema de las diversas resoluciones de pantalla. Una web se tiene que ver correctamente tanto en monitores con un ancho de 800px como de 1200px, por lo menos.
Bien, para que os hagáis una idea de como están las cosas en el mundo de las apps, en el siguiente gráfico están todas las resoluciones posibles de los dispositivos Android e iOS.
pantallas-800x400
Una locura no, mucho peor. Gracias a nuestra experiencia en webs «liquidas» (adaptables a diversos anchos), conocemos varias estrategias para superar algo este problema que podemos adaptar al diseño de videojuegos.

Lo primero es no utilizar posiciones absolutas sino relativas, es decir, en vez de posicionar los botones en coordenadas fijas x e y, lo hacemos tomando como referencia los bordes, es decir, a x distancia de la izquierda, a x distancia de arriba, etc.
La segunda es tomar como referencia un porcentaje del ancho y trabajar en base a el. En nuestro caso, lo llamamos modulo, añadimos otro modulo vertical para ciertos posicionamientos (modulov=Screen.height/10).
Todos los elementos de la interfaz están basados en dimensiones divisibles entre si en números enteros, por ejemplo tal boton es 4x de ancho por 1x de alto, tal fondo es 4x por 3x, tal rotulo mide 8x por 1x. Normalmente, las dimensiones son en numero de pixels de base 8 porque el procesador trabaja mejor con ellas, lease 16, 32, 64, etc.

Nada mas arrancar el juego definimos que «modulo» es igual, por ejemplo, al ancho de la pantalla dividido entre 20. Así, sabemos que la mitad horizontal de la pantalla esta en 10 x modulo, y el botón anteriormente citado medirá 4 x modulo de ancho y 1 x modulo de alto,  el fondo medirá 4 x modulo y 3 x modulo, etc.  El tamaño de las fuentes también lo ajustamos tomando como referencia dicho modulo. Asi se conservan las proporciones sin deformarse y se adaptan a cualquier dispositivo. El tamaño vertical también puede variar, pero siempre está seguro entre las proporciones 4:3 (no hay pantallas cuadradas) y 16:9 de los móviles y tabletas panorámicos, entonces simplemente nos aseguramos de dejar suficiente «aire» a los lados para que no se corten los elementos, algo de lo que tenemos también experiencia por nuestros trabajos en vídeo, donde se reproducía el problema de que un videoclip o cortometraje se pudiera ver bien tanto en teles «cuadradas» como panorámicas, esto se llama «área segura» (o «safe area» en inglis) y todos los elementos importantes se tenían que poner dentro de ese área.
En la siguientes capturas podéis ver como se adaptan los botones con dos proporciones «extremas» en el juego de Mah-Trakka siguiendo estas reglas, posiciones y tamaños relativos y «aire» a los lados.
capturasresol

Se puede ver que lo importante, el rotulo superior y el personaje corriendo, se ven bien. Luego ya es cuestión de probar en distintos dispositivos y retocar lo necesario, por ejemplo, en estas capturas falta definir dos anchos de boton, para textos largos o cortos.
Como siempre, recordamos que somos inexpertos y muy posiblemente existan estrategias o trucos mejores, si alguien los conoce, que los apunte en los comentarios 😉

Programando el miedo

El movimiento del personaje se basa en una serie de puntos (waypoints) sobre el escenario que va recorriendo por orden. Al principio los recorría sin más, un punto tras otro. Para evitar el problema de que la bola lo adelantara y se dirigiera hacia ella (lo que no dejaría en buen lugar su inteligencia) habíamos pensado en hacer los escenarios de forma que el recorrido fuera lineal, sin la posibilidad de que la bola pudiera adelantarle sin chafarlo.

Luego decidimos que si veía la roca delante pudiera girarse para esquivarla. Eso evitaría que se comportara como un robot, y así también podíamos hacer escenarios más abiertos y añadía jugabilidad y diversión al hacer al ladrón capaz de esquivar la bola en cierta medida. ¿como hacer que ind… perdón, el mangante viera la roca? Lo primero que pensamos fue en complicados algoritmos basados en la distancia, orientación, etc. pero al diseñador se le ocurrió una solución mucho más sencilla. Creamos un objeto triangular y lo pegamos al personaje haciendo que detectara la colisión con la bola. Este objeto seria invisible y ademas de permitir una programación muy sencilla podíamos cambiar su ancho y largo fácilmente para hacer que el personaje viera mas lejos o mas «ámplio». Si el campo de visión chocaba con la bola, sencillamente decíamos que el próximo waypoint fuera el anterior en vez de el siguiente en su lista. Añadimos un punto extra al que solo se dirigiría si hubiera llegado al primero para evitar un movimiento de ping pong si no quedaban waypoints anteriores. Se puede ver claramente en el siguiente vídeo, donde hemos iluminado el objeto campo de visión en verde.
Ademas, también se aprecia bien otro comportamiento añadido para que sea mas gracioso y dinámico, corre más cuando la roca esta cerca, ampliaremos la información sobre esto en otro post.

Cosas del progreso

En el mundo de la computación a veces sucede una curiosa paradoja, merced a la Ley de Moore. Resulta que si uno tiene que realizar un calculo que dure, pongamos, 6 meses, merece la pena esperar 4 meses porque los ordenadores habrán avanzado de forma que el calculo solo cueste un mes. Nos ha pasado algo así. Estamos realizando este juego con la versión free de Unity, ya que la versión completa, incluyendo los plugins para android e iOS cuesta 4500$ que no tenemos. En la versión free no existían sombras dinámicas, por lo que las simulamos utilizando lo que se denomina sombras «blob», es decir, proyectamos una imagen (un circulo borroso) simulando la sombra bajo el muñeco. Da bastante bien el pego, pero provoca otros problemas, como el que podéis ver en la siguiente imagen:
Image
Como podéis ver en el ovalo rojo, la sombra se proyecta sobre la pared de forma irreal. Para evitarlo, hay que desactivar la proyección de la sombra en todos los objetos que no deben tenerla, el muñeco, las paredes, etc, y dejarla activada solo en el suelo.
Bueno, preparamos el código, desactivamos los objetos, etc, y después de todo el curro, Unity saca una nueva versión free sin consultarnos e incluye sombras dinámicas «gratis».
Pues si, muy bien, pero horas de faena tiradas. De todas formas se tiran a gusto porque el juego gana mucho con las sombras dinámicas, ya no seran un manchurrón negro estático sino que se deforman con el movimiento del muñeco y queda mucho más chulo.
También han añadido luces dinámicas, lo que nos evitaría «pintar» las luces y las sombras directamente sobre las texturas, como están ahora, pero esto si que no lo vamos a cambiar porque no añade gran cosa a este juego y si que afectaría al rendimiento y eso si que nos importa. Somos pobres, nuestros dispositivos son antiguos y baratos y estaría bueno que no pudiéramos jugar en condiciones a nuestro propio juego.

Por otra parte hemos aumentado la velocidad de la bola porque se nos han quejado de que se ve lenta varios miles de personas al ver el vídeo del gameplay, y de hecho, cuando alguien juega por primera vez vemos que inclinan el cuerpo a un lado y al otro más de lo que es sano para la columna vertebral queriendo darle más velocidad.  Podeis ver estos dos cambios en el siguiente vídeo:

Un problema tonto y su solución tonta.

He pedido al programador que nos escriba unas lineas sobre algún aspecto del juego que le pueda parecer interesante para comentaros. Os traslado sus impresiones:

«Hombre, voy muy mal de tiempo, pero en fin.
Hemos tenido un pequeño problema con la cámara . Al principio el juego no contemplaba scroll, la cámara estaba fija en una posición cenital y ya está, no había problema. Después, se planteo la idea de hacer un scroll vertical para poder dar mas enjundia a las fases haciéndolas más grandes. Para mover el scroll se tomo la posición en pantalla de la bola, si estaba mas arriba del primer tercio  de la pantalla, la cámara se desplazaba hacia arriba para centrarla, mas abajo de 2 tercios, en el sentido contrario. si estaba en el tercio intermedio, se desactivaba para no marear ni afectar el rendimiento.  En las escenas cuya habitación «cabe» en la pantalla, se desactiva

Pero se planteó el siguiente problema, si nos limitábamos a mover el eje z de la cámara podía «chocar» contra el suelo como se ve en el siguiente gráfico, que esta tomado desde una vista XY, que en el argot técnico llamamos «de lado».

poblema

Lo que hice fue emparentar (ligar, atar) un objeto intermedio invisible (el cuadrado rojo) a la superficie del laberinto, y emparentar la cámara a este objeto. Cuando hiciera falta hacer scroll según la norma de los tercios anterior el objeto intermedio se movería en consecuencia sobre el eje Z de las coordenadas del laberinto y la cámara lo sigue, manteniendo siempre la misma distancia y orientación relativa sobre el plano del laberinto de forma sencilla y facil de programar.
Es posible que en siguiente gráfico se aprecie mejor lo que quiero decir.

solucion