Introducción
La industria del videojuego ha experimentado un gran empujón gracias a las posibilidades que brindan los dispositivos móviles y las nuevas características de los navegadores modernos. Entre todos los nuevos elementos, el más interesante para la programación de aplicaciones gráficas, es sin duda el objeto canvas. Su uso, apoyado en Javascript, está abriendo un nuevo mercado donde las posibilidades crecen día a día.
HTML5 parece una plataforma sólida para la programación de videojuegos. Este estándar, apoyado por algunos dispositivos como el iPad, está restando protagonismo al hasta ahora omnipresente FLASH, la solución de facto durante la última década para los contenidos multimedia en el navegador. Sin embargo, existen algunas consideraciones ha tener en cuenta cuando nos planteamos el diseño y arquitectura de un videojuego utilizando elementos como las famosas canvas. Tal es su aceptación y deseo de proyección, que incluso se han portado a navegadores antiguos como IE6 mediante librerías como ExCanvas.
De acuerdo, pero ¿qué son las famosas canvas?
Canvas es un elemento de la nueva especificación HMTL5 que permite generar gráficos de forma dinámica a partir de formas 2D o mapa de bits a través de Javascript: consiste en una región dibujable definido en el código HTML que posee atributos de altura y anchura. Mediante una API sencilla, Javascript tiene acceso a diversas funciones de dibujo a través de las cuales se pueden generar animaciones, juegos, gráficas y composiciones.
Las diferencias del elemento canvas con respecto al SVG (Scalable Vector Graphics) es que este último actúa a alto nivel ya que cada imagen dibujada mediante este estándar, resulta en un elemento definido en el DOM. Esto significa que cada vez que un atributo del objeto SVG cambia, el navegador necesita volver a renderizar la escena. Conceptualmente, canvas es un elemento a bajo nivel capaz de actualizar por sí mismo la escena sin necesidad de enviar al navegador una petición de refresco.
Pero, ¿es realmente canvas una solución válida para el desarrollo de videojuegos?
Canvas no maneja bien las imágenes
Por lo general, cuando afrontamos la etapa de diseño de un videojuego basado en dos dimensiones (la tradicional perspectiva isométrica), recurrimos a numerosas imágenes que denominamos sprites.
Estos sprites por lo general componen un mapa con todos aquellos elementos que necesitamos durante la representación de una escena, ya sean los fondos (landscapes), los objetos que se superponen encima o los personajes (characters) con todas sus posibles posturas. El código de nuestro juego deberá saber seleccionar que coordenadas de esa imagen necesita cargar en un momento dado para mostrarnos el contenido. Sin embargo, aquellos primeros sprites que aparecieran en los sitemas MSX, Atari o Amiga se consideraban nativos ya que eran dibujados por un hardware gráfico especializado. En los sistemas actuales con canvas y Javascript, es el procesador central (el intérprete) el que maneja todas las imágenes y realiza los cálculos.
El API de canvas no está optimizado para incluir estas imágenes de una manera natural. En lugar de ello, cuando introducimos un nuevo sprite, primero tiene que construirse a través del DOM mediante el método new Image(). A continuación, hay que establecer la ruta de la imagen (source), esperar a que sea descargada y finalmente renderizarla en el interior del elemento canvas.
var img = new Image(); // Se crea un objeto Image img.onload = function(){ // Código para el dibujado en el objeto canvas } img.src = 'myImage.png'; // Establecer la ruta de la imagen |
Incluso si cacheamos las imágenes, este proceso tiene un coste importante de procesado y, obviamente, un rendimiento pobre.
Una solución puede ser el embeber las imagenes convirtiéndolas en cadenas codificándolas en base64 (data : url). La ventaja de este sistema es que una vez disponible, ya no se requiere más interacción con el servidor. El punto negativo es que las imágenes no se cachean sino que permanecen en la memoria del sistema como cadenas asignadas a sus variables. A veces, dependiendo del tamaño de las imágenes, estas cadenas pueden ser extremadamente largas:
var img_src = 'data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ ZiH5BAEAAAEiGBYAOwABAEAAAEALLAAAAAALAAsiGBYA OwAAAIUhAALAAsAAAIUhA+hkiGBYAOwcuO4lmNVindo7 kcuO4lqyrIXiGBYAOw=='; |
La codificación de las imágenes podemos realizarla mediante un lenguaje de servidor como PHP utilizando la función nativa base64_encode:
<?php $img_src = "image/sample.png"; $imgbinary = fread(fopen($img_src, "r"), filesize($img_src)); $img_str = base64_encode($imgbinary); echo '<img src="data:image/jpg;base64,'.$img_str.'" />'; ?> |
Si queremos hacerlo directamente desde Javascript, tenemos que prepararnos nuestra propia función como esta aparecida en webtoolkit. Existen tambíen codificadores online como este en el que basta con poner la dirección (URL) de nuestra imagen (o cualquier otro elemento) para que nos devuelva la cadena codificada.
Una vez tengamos nuestra cadena, podemos ya incluirna directamente en nuestro (X)HMTL, una hoja de estilos CSS o un documento XML entre otros. A continuación se muestra el código necesario para cada uno de los casos:
<!-- (X)HTML --> <img alt="Embedded Image" width="600" height="436" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD..." /> |
// CSS div.image { width: 600px; height: 436px; background-image: url(data:image/jpeg;base64,/9j/4AAQSBAQEASABIAAD...); } |
<!-- XML --> <image> <title>An Image</title> <link>http://www.your.domain</link> <url>data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...</url> </image> |
El redibujado basado en canvas es costoso
Por lo general, un videojuego se basa en el desplazamiento de elementos a través de la ventana (viewport) renderizada. Además, lo deseable es siempre conseguir que esa ventana sea lo más grande posible, a pantalla completa si se puede. Esto conlleva que el movimiento de un personaje exija el redibujado de todo el área que libera y aqeul por el que va desplazándose. Lógicamente, a mayor tamaño de ventana, mayor será la zona a redibujar y en un elemento canvas, el proceso es extremadamente lento.
Incluso implementar un algoritmo de redibujado es técnicamente complicado. El motor debe realizar las siguientes operaciones para lograr la sensación de movimiento fluido:
- Calcular el área que será redibujada con el desplazamiento.
- Redibujar dicho área con todos los elementos estáticos durante el proceso. El motor requiere para esto registrar las posiciones de todos los elementos en pantalla en un momento preciso.
- Una vez limpiada la zona, hay que redibujar sobre todo lo anterior la nueva posición del elemento allá donde corresponda.
Este proceso, como comentábamos, es técnicamente complejo pero viable: de hecho es así como funcionan la mayoría de motores 2D. El principal problema lo encontramos en el coste de procesado de dicho proceso y que se traduce en un rendimiento pobre en el navegador.
Una última consideración es que bajo el elemento canvas, debe ejecutarse el código Javascript sobre el que se despliega el motor de renderizado. Pese a que los navegadores cada vez implementan intérpretes más rápidos, el coste de ejecución de un código complejo es pesado en términos de rendimiento. El mal uso del lenguaje y de algunas librerías populares como jQuery o Mootols solo añade una capa más de complejidad que retrasa el conjunto. Si además de todo, incluimos algoritmos para emular motores de física o partículas, el rendimiento puede caer hasta un mínimo inaceptable. A modo ilustrativo, si queremos informarnos de todo lo que ocurre en un navegador cuando tiene que ejecutar código Javascript, podemos ojear el artículo de Christoffer Blizzard donde explica de forma rápida todo el proceso en Firefox.
Conclusión
Personalmente, apuesto porque los juegos terminarán desarrollándose en Javascript utilizado como lenguaje estándar para la gran mayoría de dispositivos móviles y sus posibilidades portadas al escritorio. Librerías como node.js, favorecen la concurrencia de miles de ejecuciones simultáneas que funcionarían perfectamente en los juegos en red.
Sin embargo, aún es pronto para que empecemos a ver motores potentes: el V8 Javascript Engine de Google es, a día de hoy, la solución más rápida para ejecutar Javascript, seguida de cerca por el JägerMonkey de Mozilla y el Carakan de Opera. El próximo hito es aprovechar las propiedades del hardware nativo para liberar recursos y ofrecer una capacidad de procesado varias veces superior al actual. La página Chrome Experiments ofrece ejemplos increíbles que auguran un gran futuro para la industria del videojuego.
Si a esto le sumamos las tendencías que parece dominarán el desarrollo web en 2011, el escenario estará preparado un importante salto cualitativo. De momento, las posibilidades actuales del nuevo estándar HTML5 no ofrecen las herramientas necesarias para hacerle frente a un maduro FLASH o a un applet de Java.
A día de hoy, canvas no es una opción válida para un desarrollo complejo de videojuegos: le falta madurez. Sin embargo, brinda un escenario perfecto para la experimentación adelantándonos las posibilidades que vendrán próximamente.
Más información:
Mozilla, Tutorial canvas y uso de imágenes
Wikipedia, Comparación del elemento canvas en distintos navegadores
JorisO, Using images in HTML5 Canvas
Aún no tenemos debug!
Ningún marciano se ha comunicado.