Cut the Rope es divertido y entrañable, así que se nos ocurrió una idea: vamos a hacer que mucha más gente pueda disfrutar de él publicándolo en la web de manera que se pueda jugar desde el navegador, aprovechando la potencia de HTML5.
Puedes jugar con la versión de Cut the Rope para HTML5 en www.cuttherope.ie.
En nuestra opinión, la versión para HTML5 hace más divertida la web y nos demuestra los avances en materia de soporte de estándares que se han introducido en la última versión de Internet Explorer. Teniendo en cuenta esto, hemos querido compartir con vosotros algunos de los detalles más jugosos de tipo técnico que están en la trastienda de este proyecto y que seguramente os van a ayudar a crear vuestros propios sitios HTL5 y en definitiva, ¡os abrirán el camino hacia la tienda de Windows 8!
Cut the Rope funcionando en IE9 como aplicación HTML5 después del porting a partir del código fuente iOS.
Captura de pantalla de uno de los niveles diseñados específica y exclusivamente para esta versión.
Migración desde Objective-C a JavaScript
Al migrar Cut the Rope a una nueva plataforma queríamos asegurarnos de que se mantenían sus características exclusivas en física, movimiento y personalidad de la experiencia de usuario. Así, desde el principio optamos por proceder a un porting del juego a partir de la versión original de iOS (en vez de rescribirlo desde cero). Empezamos el proyecto con una investigación exhaustiva del código base Objective-C. Sin duda se trata de un juego amplio y complejo. La versión iOS original contiene unas 15.000 líneas de código (excluyendo las librerías) y la parte más complicada es la que corresponde a los motores de física, animación y dibujo. Son de por sí complejos, pero aún se complican más por el hecho de que existe una estrecha interconexión entre los tres y están muy optimizados.Era, sin duda, una tarea de titanes: meter este código en un navegador sin perder su personalidad única y la esa altísima calidad que tanto valoran los jugadores. Para ello decidimos apostar por JavaScript.
En el pasado, JavaScript tenía fama de ser un lenguaje lento. Francamente, esto podía ser cierto al principio: los antiguos motores de JavaScript estaban diseñados para resolver tareas sencillas de intérprete de scripts (de ahí su nombre). Pero hoy en día los motores de JavaScript están muy optimizados. Incluyen funcionalidades como la compilación JIT (Just-In-Time), con la que se logran velocidades de trabajo cercanas al código binario de cualquier ejecutable
Pero aparte de todo esto, sabemos que la codificación para JavaScript es distinta de y requiere un enfoque muy diferente de- lo que es la codificación para lenguaje compilado. Durante el porting del juego desde Objective-C éramos plenamente conscientes de que tendríamos que abordar toda una serie de cambios y optimizaciones.
Un caso evidente es la falta de estructuras (structs) en JavaScript. Las estructuras del lenguaje C son agregados ligeros de propiedades relacionadas entre sí. Para un objeto de JavaScript resulta sencillo mantener una serie de propiedades, pero hay diferencias sustanciales entre esta aproximación y una estructura. La primera es que los structs se copian siempre que se asignan a una variable o se pasan a una función. Así, una función escrita en un lenguaje como Objective-C puede modificar un struct que se le ha pasado como parámetro sin afectar al valor original del llamador. Incluso dentro de una función, la asignación de un struct a una variable distinta se hace copiando los valores. Los objetos de JavaScript, por su parte, se pasan por referencia, de manera que cuando una función modifica un objeto enviado como parámetro, los cambios se reflejan también en el llamador.
Una forma sencilla de reproducir el funcionamiento original de los structs consiste en crear copias de los objetos JavaScript cuando se realizan asignaciones o se pasan como parámetros. En los lenguajes nativos esto supone un trabajo adicional mínimo con respecto al uso de structs. La creación de un objeto en JavaScript es bastante más costosa, por lo que tuvimos que poner un cuidado especial en reducir todo lo posible el número de asignaciones memoria. En concreto en el caso de las asignaciones de memoria, siempre que era posible tratábamos de copiar las propiedades individualmente en vez de crear instancias completas de los objetos.
Otro ejemplo es la esencia de orientación a objetos del código Objective-C. En lugar del concepto tradicional de herencia basada en objetos, JavaScript nos ofrece un modelo de herencia basada en la propiedad Prototype. Es una forma muy simplificada de herencia de objetos que resulta incompatible con los lenguajes orientados a objetos tradicionales, como es Objective-C. Por suerte, disponemos de una gran variedad de librerías de clases que nos pueden servir para escribir código con el mismo estilo de la programación orientada a objeto (OOP) en JavaScript. Utilizamos una librería muy sencilla que ha escrito John Ressig (un gurú del proyecto jQuery). (Recordemos que ECMAScript5, la especificación de la última versión de JavaScript, dispone también de soporte para clases, pero hemos preferido no utilizarlo porque no tenemos mucha experiencia con esta versión del lenguaje y el plazo que nos habíamos fijado para el desarrollo era muy escaso).
Además del porting desde Objective-C a Javascript, teníamos también que migrar el código de gráficos desde OpenGL al API Canvas de HTML5. Puedo resumirlo diciendo que ha un trabajo realmente bueno. El Canvas es una superficie de presentación gráfica con una velocidad asombrosa, sobre todo en un navegador donde esta API se acelera por hardware (como sucede en Internet Explorer 9).
Ejemplo de dibujo de las cuerdas con líneas combadas utilizando el API Canvas.
De forma sorprendente, nos encontramos unas cuantas áreas en donde el Canvas ofrece más funcionalidad que la versión OpenGL ES que se había utilizado para la versión de móviles de Cut the Rope. Uno de estos casos es el dibujo de las líneas con efecto antialiasing. El dibujo de las líneas efecto antialiasing en OpenGL exige la descomposición de la línea en una banda formada por triángulos yuxtapuestos e ir reduciendo la opacidad de los extremos hasta lograr la transparencia total. El canvas de HTML5 gestiona automáticamente el antialiasing de las líneas cuando se dibujan utilizando su API de trazo de línea. Esto suponía tener que eliminar parte del código de la versión para OpenGL. Al eliminar el array de vértices de triángulos que tenía el código de OpenGL conseguimos además mejorar el rendimiento sobre la versión original basada en el dibujo de líneas utilizando el procedimiento de la descomposición en triángulos.
Al final teníamos casi 15.000 líneas de código para ejecutar en el navegador (que se han reducido al mínimo, de manera que si ves el código fuente en el navegador, encontrarás muchas menos líneas). Previendo la complejidad que suponía tanto código, Denis Morozov (el Director de Desarrollo de ZeptoLab) nos hizo en un primer momento una inocente pregunta: ¿podría ofrecer HTML5 la velocidad y el rendimiento que requiere este juego?
Para darle respuesta nos marcamos un hito inicial de evaluación de rendimiento, en el cual teníamos que centrar nuestro esfuerzo en conseguir una versión reducida a tan solo las partes más críticas del juego en ejecución. En otras palabras: queríamos comprobar qué aspecto tendrían las cuerdas y si podríamos hacer funcionar este complejo motor de física dentro del navegador.
Rendimiento
Al cabo de tres semanas de trabajo teníamos ya lista la parte básica de los motores de física y dibujo con un timer sencillo que gobernaba la animación, Podíamos lanzar un par de cuerdas, una estrella y un sprite Om Nom dentro de la escena del juego. ¡Avanzábamos!. A la cuarta semana ya habíamos integrado una interacción básica con el ratón y con ella ya se podía jugar de verdad. Mientras tanto, fuimos midiendo el rendimiento, pero queríamos que el equipo de ZeptoLab nos diera su opinión.Cuando enviamos nuestro código a ZeptoLab para que lo vieran, se sorprendieron gratamente con el rendimiento (en concreto la velocidad y la suavidad de los movimientos en tiempo de juego) que veían en los navegadores más modernos. A decir verdad, durante un rato contuvimos la respiración. Esperábamos que JavaScript fuera rápido, pero los cálculos de física eran muy intensivos y tenían que hacerse en tiempo real. Es un excelente ejemplo de cómo los prejuicios habituales acerca de la lentitud de JavaScript resultan ser falsos. La última generación de motores JavaScript es increíblemente veloz.
En este caso empezamos a probar el juego en Internet Explorer 9. Cuando se carga el juego, el motor de JavaScript de IE9, llamado Chakra, precompila el código en un thread en segundo plano, igual que lo haría un compilador de otro lenguaje, por ejemplo Objective-C o C++. Después, en tiempo real, envía el código compilado (binario) al thread del juego para su ejecución. El resultado es una velocidad prácticamente igual que la del código nativo. Lo asombroso es que nos lo da el motor de JavaScript sin tener que hacer nada especial en el código.
Resultados de las pruebas de tasa de frames en las primeras fases del proyecto (nótese que las tasas de frame se truncan a 60FPS)
Nuestra apuesta por JavaScript ya estaba dando sus primeros frutos, así que centramos nuestra atención hacia el hardware y los navegadores. Con la pila de restitución gráfica de Internet Explorer, acelerada por gráficos y nuestra anterior experiencia con Disney Tron y otros sitios web basados en HTML5, no nos quedaba ninguna duda de que el juego podría funcionar perfectamente en nuestras máquinas de prueba. Conseguimos sin dificultad nuestra marca objetivo de 60 FPS (frames por segundo). Pero queríamos estar seguros de que el juego podría funcionar también en otros navegadores. Esto es lo que pudimos ver después de hacer algunas pruebas preliminares.
Basándonos en estos resultados, nos pusimos como objetivo mínimo el valor de 30 FPS. Decidimos que cuando el navegador bajara de este límite, avisaría al usuario. Podría seguir jugando a este juego, pero teníamos que avisarle de que no se vería bien. Así nos garantizamos el soporte para la inmensa variedad de hardware y software que tenemos en la calle y que íbamos a ofrecer la mejor experiencia posible a los visitantes de la web que quisieran disfrutar del juego.
Hay dos cosas que no quiero dejar pasar por alto. La primera, que la versión actual del juego funciona mejor en PCs y Macs de sobremesa con ratón. No hemos incorporado soporte para la interfaz táctil aún, pero es algo que tenemos previsto para futuras versiones.
La segunda es que la versión actual de Chrome (versión 16) tiene problemas reconocidos relacionados con la reproducción de contenidos multimedia que hacen que el sonido se escuche de manera imprevisible en nuestro juego Cut the Rope. Hemos estado viendo posibles soluciones y hemos tratado de recodificar los archivos de audio en distintos formatos (incluyendo WebM), pero no hemos encontrado un formato o configuración MIME ni nada que realmente resuelva este problema de manera fiable. Tiene toda la pinta de que es un fallo del navegador, reconocido por ellos además. Pero sobre todo, el juego se puede ejecutar y podemos disfrutar de él aun con los problemas de intermitencia del audio. A la vista de esto, mientras que podemos afirmar que los usuarios de Internet Explorer 9 pueden tener una excelente experiencia sin plug-ins, los usuarios de Chrome y en algunos casos de Firefox pueden verse afectados por el problema con el audio, pero ya os adelanto que les hemos preparado un fallback hacia el complemento de Flash para asegurarnos de que funcionan los efectos de sonido y la música.
Las herramientas
Una de las cosas buenas de HTML5 es que no necesitamos aprender un lenguaje nuevo para tener a nuestro alcance toda la potencia de esta tecnología. Si ya te manejas con JavaScript, ya puedes acceder a todo lo que es capaz de hacer un navegador moderno. ¡Puedes incluso crearte un juego como este!Editor de Código y Entorno de Desarrollo
Visual Web Developer 2010 Express es una descarga gratuita y un excelente editor, incluso para los desarrolladores web más expertos.
Captura de pantalla del perfilador que muestra una que se está asignando una cantidad de tiempo desproporcionada a Calc2PointBezier, una función utilizada para calcular las posiciones de los trozos de cuerda.
Tenemos a nuestra disposición algunas herramientas de gran calidad que nos facilitan el trabajo con JavaScript y HTML5. Gran parte de nuestro desarrollo se hizo con Visual Web Developer 2010 (la versión "express" se puede descargar aquí de forma gratuita). Se trata de un editor web ciertamente robusto, con función de autocompletar para JavaScript y CSS. Lo bueno es que ¡es gratis! Casi todas nuestras pruebas las hemos hecho con Internet Explorer 9 en Windows 7 y de vez en cuando también en Firefox, Chrome, Safari y en la preliminar de desarrollo de Internet Explorer 10. En general, los navegadores más conocidos ofrecen una implementación consistente de las funciones de HTML5 que hemos utilizado y, en la mayoría de los casos, todo lo que hemos probado en Internet Explorer 9 ha funcionado directamente en todos los demás también.
¡Echa un vistazo a nuestro Cargador de Recursos!
Cut the Rope tiene un estilo visual muy personal y detallado, con montones de imágenes y contenidos de vídeo y audio, que se muestran con un coste muy pequeño. El resultado es que todo el juego es mucho más grande que un sitio web normal. Todo junto viene a ocupar unos 6 Mb (comparado con los 200 300 Kb de un sitio web típico). Tanta cantidad de contenidos multimedia tardan un poco en descargarse y no podemos empezar con el juego hasta que todo esto ya está listo en el ordenador. En una página web tradicional no suele ser un trauma el que falte alguna imagen o dos, pero el API canvas de HTML5 (drawImage) falla si no encuentra el archivo de la imagen.Para resolver este problema preparamos un cargador de recursos que descarga todo el contenido que necesitamos para la página y nos ofrece buena información a medida que se descargan los archivos. Este pequeño fragmento de código hace un montón de cositas:
- Se adapta a la forma peculiar que emplea cada navegador para manejar las descargas y para informar al usuario del progreso.
- Nos permite decidir con buen criterio el orden de descarga de cada elemento (podemos empezar con los archivos más grandes, por ejemplo, o quizá convenga más que se descarguen antes las imágenes del menú y después las del juego).
- Por ultimo, nos devuelve eventos inteligentes a medida que van llegando estos contenidos, de modo que podemos ver el progreso en pantalla o incluso empezar a jugar con alguna parte del juego, en cuanto el primer grupo de descarga ha terminado.
Herramientas de rendimiento en Internet Explorer
Otra herramienta indispensable en el proceso de desarrollo ha sido el Perfilador de JavaScript que viene con Internet Explorer 9. El proceso de perfilado nos permite descubrir puntos conflictivos y cuellos de botella en el código. En aquella versión primera que os comenté para evaluar el rendimiento, en un momento dado tuvimos que emplearlo a fondo cuando descubrimos que en algunas máquinas nos quedábamos en 20 ó 30 PFS y no salíamos de ahí. Empezamos revisando un poco el código, pero no dábamos con el problema. Cargamos el juego con el perfilador y al momento pudimos ver que perdía un montón de tiempo dentro de la funciónsatisfyConstraints(). Esa función calcula algunas ecuaciones de la parte de física de las cuerdas. En la implementación de Objective-C que habíamos portado, la función estaba escrita de forma recursiva, pasando un nuevo objeto a cada una de las llamadas que se hacía a sí misma.
Con ayuda de Microsoft decidimos sustituir la función recursiva con una versión iterativa desempaquetada del mismo código. El resultado fue asombroso. ¡Conseguimos multiplicar por 10 el rendimiento en todos los navegadores!. Confieso que jamás lo habríamos podido detectar sin las herramientas de perfilado de Internet Explorer 9.
¿Lo siguiente?
En la conferencia BUILD del pasado mes de septiembre, Microsoft presentó en sociedad una versión preliminar de desarrollo de Windows 8. Con este anuncio la historia de HTML5 se ha puesto aún más interesante, ya que las aplicaciones estilo Metro se pueden crear utilizando una gran variedad de herramientas de desarrollo, entre ellas HMTL5. Esto quiere decir que los desarrolladores web pueden coger código escrito para la web y portarlo de manera sencilla y transparente a Windows 8. La inversión en experiencias online avanzadas hoy nos puede dar grandes beneficios más adelante, cuando funcione la tienda de Windows Store.En realidad, con muy poco esfuerzo adicional, hemos podido migrar esta experiencia HTML5 a una aplicación estilo Metro para Windows 8. Puedes leer más sobre Cut the Rope y su integración con Windows Store en este artículo del blog.
Estamos muy contentos viendo lo que los desarrolladores pueden hacer hoy con HTML5. Podéis descargar Internet Explorer 9 y visitar otros sitios Web realmente bonitos en www.beautyoftheweb.com, y también os recomiendo que descarguéis la Preliminar de Desarrollo de Windows 8 desde dev.windows.com.
Giorgio Sardo
Evangelista Técnico Senior | HTML5 e Internet Explorer