26 de Febrero, 2013

Experimentando con canvas

No hago más que leer acerca de proyectos interesantes que nos prometen tiempos excitantes para la web, con la especificación de HTML5 como candidate recommendation desde finales del año pasado y diferentes partes implementadas a buen nivel por varios navegadores; además de Javascript cada vez pegando más fuerte.

No me gusta el lenguaje, tiene demasiados WTF, y muchos inconvenientes que hacen bastante tedioso usarlo para cosas grandes, sobretodo si disfrutas de mejor calidad de vida trabajando con otros lenguajes ;).

El caso es que he ojeado alguna vez alternativas para programar juegos con Javascript, pero nunca he llegado muy lejos con ninguna de ellas. Quizás sea por la barrera de entrada (hay que saber manejar el framework antes de empezar a hacer algo), o porque es fácil dispersarse :).

Hace un par de fines de semana me puse a investigar el elemento canvas, directamente sin ningún tipo de arnés (¡y sin red! :D), empezando con el tutorial de canvas de Mozilla. Y bueno, sin ser genial, se puede dibujar directamente mediante un interfaz que enseguida me resultó familiar.

Pues ahí estaba yo cacharreando con el canvas y se me ocurrió hacer un cutre-motor que imitara el falso 3D de juegos tipo Dungeon Master. Es bastante más sencillo de lo que parece: hay un limitado número de piezas que vamos poniendo en pantalla en el orden adecuado siguiendo el algoritmo del pintor (dibujamos las piezas de más lejos a más cerca respecto a la posición del espectador).

Ya lo hice una vez hace unos cuantos años, pero me aburrí pronto porque las piezas para construir el escenario en 3D son complicadas de dibujar (como siempre, no soy un artista), así que se me ocurrió: ¿por qué no usar raycasting? Se trata de una técnica para dibujar un pseudo-3D que se usaba en juegos hace años, destacando Wolfenstein 3D y poco más tarde Doom (hay muchos más, pero con estos dos queda claro a qué me refiero).

La idea es que crear una textura que pegar en los muros es relativamente fácil (mucho más que dibujar en perspectiva las piezas del escenario como en Dungeon Master), y además la transición entre posiciones del jugador puede ser suave en lugar de los saltos habituales de los juegos tipo DM (hablamos de finales de los años 80 :P). La cuestión era si canvas me daría suficiente rendimiento para implementar un raycasting sencillo. La respuesta es: casi.

Un calabozo
Prueba usando un degradado para suelo y techo, con iluminación en profundidad

Tras leer un par de tutoriales sobre raycasting (Ray-Casting Tutorial For Game Development And Other Purposes es el más clásico, usando ángulos; y Lode's Computer Graphics Tutorial: Raycasting que emplea la técnica del Digital Differential Analysis, más sencillo de implementar en mi opinión), me puse a hacer pruebas y los muros se pueden dibujar a una velocidad más que aceptable gracias a que se dibujan linea a linea horizontalmente, y se puede optimizar el proceso copiando trozos de la textura en bloques de 1px de ancho por el alto del segmento de muro que toca dibujar, dejando que el navegador escale la textura por nosotros.

Más o menos la misma idea se puede aplicar para los sprites (o lo que es lo mismo: cualquier objeto que no es un muro), aunque el efecto resultón que consigo para cambiar la iluminación de los muros en base a la distancia no se puede implementar igual porque los objetos tienen una parte transparente que estropea el tintado (se ve una mancha oscura alrededor del objeto).

La parte del casi es porque la técnica para dibujar el techo y el suelo es diferente a la empleada con los muros, y resulta demasiado costosa porque no he encontrado atajos para hacerlo rápidamente con el canvas (hay que proyectar punto a punto, y es muy lento).

Suelos y techos
Muros y suelos con textura, pero a menos de un cuadro por segundo :(

Al final he dado con unos degradados que no quedan del todo mal, y creo que tengo el motor con la suficiente funcionalidad como para hacer un juego; aunque me faltan cosas, como hacer que el jugador interaccione con los objetos (click en pantalla, proyectar las coordenadas y trazar un rayo hasta que impacte -o no- con un objeto; ¡fascinante! :D) o implementar puertas. A priori yo diría que se puede hacer, porque en mis pruebas con Chromium y Firefox consigo alrededor de 30 FPS, que es más que suficiente.

Seguiré comentando el experimento, porque parece que la cosa se me está alargando un poco más allá de proyecto de fin de semana, y quién sabe si puede salir algo interesante de todo esto :P.

Actualización: he publicado una demo de raycasting en canvas HTML5.

Anotación por Juan J. Martínez, clasificada en: javascript, rpg.

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.

Algunas anotaciones relacionadas: