Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Técnicas de dibujo 2D y bibliotecas para juegos web
Durante mucho tiempo, solo había una manera realmente de realizar juegos web interactivos: Flash. Nos guste o no, Flash tenía un sistema de dibujo rápido. Todo el mundo lo empleaba para crear animaciones, aventuras de apuntar y hacer clic y todo tipo de otras experiencias.
Cuando los exploradores se alineaban en estándares web con HTML5, había una verdadera explosión de opciones para el desarrollo de gráficos rápidos y de alta calidad, sin necesidad de complementos. En este artículo se presentará una pequeña muestra de los métodos de dibujo, así como de las tecnologías subyacentes y de algunas bibliotecas para facilitar su uso. No abarcaré las bibliotecas diseñadas específicamente para juegos. Hay tantas de ellas que voy a guardar ese tema para otro artículo.
Estándares de dibujo
Con la aparición de HTML5, surgieron tres formas comunes de dibujar en 2D: el Document Object Model (DOM), el lienzo y el formato de Scalable Vector Graphics (SVG). Antes de pasar a la encuesta de bibliotecas que utilizan estas tecnologías, analizaré cómo funciona cada una para comprender mejor los inconvenientes de cada método.
No es sorprendente que la manera más básica de dibujar gráficos en HTML es ciertamente con HTML. Al crear varios elementos de imagen o en segundo plano y utilizar una biblioteca como jQuery, puede crear rápidamente duendecillos que puede mover sin volver a dibujar la escena. El explorador lo hará por usted. Este tipo de estructura a menudo se denomina gráfico de escena. En el caso del HTML, el gráfico de escena es el DOM. Dado que va a utilizar CSS para aplicar estilo a los duendecillos, también puede utilizar transiciones y animaciones de CSS para agregar un movimiento suave a la escena.
El problema principal con este método es que depende del representador de DOM. Esto puede ralentizar las cosas cuando tenga una escena compleja. No recomiendo usar más de unos pocos cientos de elementos. Por tanto, algo más complejo que un juego de plataforma o de tres partidas podría tener problemas de rendimiento. Y un aumento repentino en el número de elementos, al igual que en un sistema de partículas, puede provocar interrupciones en la animación.
Otro problema con este método es que necesita usar CSS para los elementos de estilo. Dependiendo de cómo escriba CSS, puede ser decentemente rápido o bastante lento. Por último, escribir código destinado a HTML puede ser difícil de mover a un sistema diferente, como C++ nativo. Esto es importante si desea trasladar el juego a algo similar a una consola. Este es un resumen de las ventajas:
- Compilaciones en la estructura básica de una página web
- jQuery y otras bibliotecas facilitan el movimiento de elementos
- Los duendecillos son relativamente fáciles de configurar
- Sistema de animación integrado con transiciones y animaciones CSS
Y un resumen de los inconvenientes:
- Muchos elementos pequeños pueden ralentizar su trabajo
- Necesidad de usar CSS para los elementos de estilo
- Ninguna imagen de vector
- Puede ser difícil el traslado a otras plataformas
Lienzo de HTML5
El elemento canvas soluciona muchos de los inconvenientes. Proporciona un entorno de representación de modo inmediato: una franja sin formato de píxeles. Le indica qué dibujar en JavaScript y lo dibuja inmediatamente. Dado que está convirtiendo los comandos de dibujo en píxeles, puede apilar con rapidez una larga lista de comandos de dibujo sin dificultar el sistema. Puede dibujar geometría, texto, imágenes, degradados y otros elementos. Para leer más información acerca del uso del lienzo para juegos, consulte el artículo de David Catuhe en bit.ly/1fquBuo.
Entonces, ¿cuál es el inconveniente? Debido a que el lienzo olvida lo que dibujó en el momento en que termina, tiene que volver a dibujar la escena usted mismo cada vez que desee cambiarla. Y si desea modificar una forma de una manera compleja, como el plegado o la animación, debe realizar los cálculos y volver a dibujar el elemento. Esto significa que necesita mantener muchos datos acerca de la escena en sus propias estructuras de datos. Realmente no es gran cosa, teniendo en cuenta que hay bibliotecas que facilitan esta tarea. Si realmente desea hacer algo personalizado, tenga en cuenta que el lienzo no conserva información para usted. Por último, el lienzo no incluye animaciones. Tendrá que dibujar la escena en pasos sucesivos para realizar una animación suave. Este es un resumen de las ventajas:
- Dibuja directamente; las escenas pueden ser más complejas
- Admite muchos de los distintos elementos visuales
Y un resumen de los inconvenientes:
- No hay memoria inherente de la escena; deberá crearla usted mismo
- Las animaciones y las transformaciones complejas deben realizarse manualmente
- Ningún sistema de animación
SVG: Scalable Vector Graphics
Como un marcado basado en XML para describir los elementos visuales 2D, SVG es similar a HTML. La diferencia clave es que SVG está pensada para dibujo, mientras que HTML está diseñada principalmente para texto y diseño. Como tal, SVG tiene algunas capacidades eficaces de dibujo como formas suaves, animaciones complejas, deformaciones e incluso filtros de imágenes como el desenfoque. Al igual que el HTML, SVG tiene una estructura de gráficos de escena, para que pueda leer detenidamente elementos SVG, agregar formas, cambiar sus propiedades y no preocuparse de volver a dibujar todo. El explorador lo hará por usted. En el vídeo "Trabajar con SVG en HTML5" de Channel 9 (bit.ly/1DEAWmh) se explica más información.
Al igual que el HTML, las escenas complejas pueden perjudicar al SVG. SVG puede controlar cierta complejidad, pero no puede igualar lo bastante la complejidad ofrecida al usar canvas. Además, las herramientas para manipular SVG pueden ser complejas, aunque hay otras herramientas para simplificar el proceso. Este es un resumen de las ventajas:
- Muchas opciones de dibujo como superficies curvas y formas complejas
- Estructuradas sin necesidad de volver a dibujar
Y un resumen de los inconvenientes:
- La complejidad puede perjudicarle
- Difícil de manipular
Bibliotecas de dibujos 2D
Ahora que conoce los estándares disponibles para dibujar en la web, echaremos un vistazo a algunas bibliotecas que pueden facilitar la animación y el dibujo. Merece la pena mencionar que rara vez se dibuja algo sin hacer nada más con el dibujo. Por ejemplo, a menudo necesita gráficos para reaccionar a la entrada. Las bibliotecas ayudan a facilitar las tareas comunes asociadas al dibujo.
KineticJS ¿Desea un gráfico de escena para el lienzo? KineticJS es una biblioteca de lienzos muy eficaz que comienza con un gráfico de escena y agrega más funcionalidad. En la línea de base, KineticJS le permite definir capas en el lienzo con formas para dibujar. Por ejemplo, en la Figura 1 se muestra cómo dibujar un círculo rojo simple con KineticJS.
Figura 1 Dibujo de un círculo con KineticJS
// Points to a canvas element in your HTML with id "myCanvas"
var myCanvas = $('#myCanvas');
var stage = new Kinetic.Stage({
// get(0) returns the first element found by jQuery,
// which should be the only canvas element
container: myCanvas.get(0),
width: 800,
height: 500
});
var myLayer = new Kinetic.Layer({id: “myLayer”});
stage.add(myLayer);
var circle = new Kinetic.Ellipse({
// Set the position of the circle
x: 100,
y: 100,
// Set the size of the circle
radius: {x: 200, y: 200},
// Set the color to red
fill: '#FF0000'
});
myLayer.add(circle);
stage.draw();
Necesitará llamar a la última línea de la Figura 1 cada vez que desee que la escena se vuelva a dibujar. KineticJS hará el resto recordando el diseño de la escena y garantizando que todo está dibujado correctamente.
Hay algunos aspectos interesantes en KineticJS que lo hacen bastante eficaz. Por ejemplo, la propiedad de relleno de un objeto puede ser bastantes cosas, incluido un degradado:
fill: {
start: {x: 0, y: 0},
end: {x: 0, y: 200},
colorStops: [0, '#FF0000', 1, '#00FF00']
},
O una imagen:
// The "Image" object is built into JavaScript and
// Kinetic knows how to use it
fillPatternImage: new Image('path/to/an/awesome/image.png'),
KineticJS también tiene un sistema de animación, que le permite mover objetos mediante la creación de un objeto de animación o mediante un objeto de interpolación para las propiedades de transición de las formas de la escena. En la Figura 2 se muestran ambos tipos de animaciones.
Figura 2 Animaciones con KineticJS
// Slowly move the circle to the right forever
var myAnimation = new Kinetic.Animation(
function(frame) {
circle.setX(myCircle.getX() + 1);
},
myLayer);
// The animation can be started and stopped whenever
myAnimation.start();
// Increase the size of the circle by 3x over 3 seconds
var myTween = new Kinetic.Tween({
node: circle,
duration: 3,
scaleX: 3.0,
scaleY: 3.0
});
// You also have to initiate tweens
myTween.play();
KineticJS es eficaz y se usa ampliamente, especialmente para juegos. Consulte el código, los ejemplos y la documentación en kineticjs.com.
Paper.js Paper.js proporciona más que simplemente una biblioteca para simplificar el dibujo en el lienzo. Proporciona una versión ligeramente modificada de JavaScript denominada PaperScript para simplificar las tareas de dibujo comunes. Al incluir PaperScript en el proyecto, vincúlelo a él como lo haría con un script normal, simplemente con un tipo de código diferente:
<script type=“text/paperscript" src=“mypaperscript.js”>
Esto permite a Paper.js interpretar el código de forma ligeramente diferente. Realmente solo hay dos piezas para esto. En primer lugar, PaperScript tiene dos objetos integrados, denominados Punto y Tamaño. PaperScript incluye estos objetos para uso común en sus funciones y proporciona la capacidad de agregar, restar y multiplicar directamente estos tipos. Por ejemplo, para mover un objeto en PaperScript, podría hacer lo siguiente:
var offset = new Point(10, 10);
var myCircle = new Path.Circle({
center: new Point(300, 300),
radius: 60
});
// Direct addition of Point objects!
myCircle.position += offset;
Lo segundo que Paper.js interpreta de manera diferente es la respuesta a eventos. Considere la situación de que escribe el código siguiente en JavaScript:
function onMouseDown(event) {
alert("Hello!");
}
Esto no hará nada porque la función no está enlazada a los eventos de ningún elemento. Sin embargo, al escribir el mismo código en PaperScript, Paper.js detectará automáticamente esta función y la enlazará al evento down del mouse. Encontrará más información acerca de esto en paperjs.orgí.
Fabric.js Fabric.js es una biblioteca de lienzo lleno de características, con la capacidad de combinar una serie de formas y efectos avanzados en una página web sin mucho código. Entre las características relevantes se incluyen filtros de imágenes, como la eliminación del fondo, las clases personalizadas para realizar sus propios objetos compuestos y soporte técnico para "dibujo libre" donde solo puede dibujar en el lienzo en varios estilos. Fabric.js es similar a KineticJS en el sentido de que tiene un gráfico de escena, excepto con una estructura más concisa, que algunos usuarios prefieren. Por ejemplo, no necesitará volver a dibujar la escena:
var canvas = new fabric.Canvas('myCanvas');
var circle = new fabric.Circle({
radius: 200,
fill: '#FF0000',
left: 100,
top: 100
});
// The circle will become immediately visible
canvas.add(circle);
No es una gran diferencia, pero Fabric.js proporciona controles de representación específica que combinan el nuevo dibujo automático y manual. Para dar un ejemplo, el escalado de un círculo en Fabric.js tiene el siguiente aspecto:
circle.animate(
// Property to animate
'scale',
// Amount to change it to
3,
{
// Time to animate in milliseconds
duration: 3000,
// What's this?
onChange: canvas.renderAll.bind(canvas)
});
Al animar algo en Fabric.js, tendrá que indicarle qué hacer cuando cambia un valor. Para la mayor parte, desea que vuelva a dibujar la escena. Eso es a lo que canvas.renderAll.bind(canvas) hace referencia. Dicho código devuelve una función que representará toda la escena. Sin embargo, si está animando muchos objetos de este modo, la escena se volvería a dibujar innecesariamente una vez para cada objeto. En su lugar, puede suprimir el volver a dibujar toda la escena y volver a dibujar las animaciones usted mismo. La Figura 3 muestra este enfoque.
Figura 3 Control de nuevo dibujo más estricto en Fabric.js
var needRedraw = true;
// Do things like this a lot, say hundreds of times
circle.animate(
'scale',
3,
{
duration: 3000,
// This function will be called when the animation is complete
onComplete: function() {
needRedraw = false;
}
});
// This function will redraw the whole scene, and schedule the
// next redraw only if there are animations going
function drawAnimations() {
canvas.renderAll();
if (needRedraw) {
requestAnimationFrame(drawAnimations);
}
}
// Now draw the scene to show the animations
requestAnimationFrame(drawAnimations);
Fabric.js proporciona mucha personalización, para poder optimizar el dibujo solo cuando lo necesita. Para algunos, eso puede ser difícil de controlar. Sin embargo, para muchos juegos complejos, puede ser una característica fundamental. Consulte más información en fabricjs.com.
Raphaël Raphaël es una biblioteca útil de SVG que elimina la mayor parte de la complejidad de tratar con SVG. Raphaël utiliza SVG cuando está disponible. Cuando no lo está, Raphaël implementa SVG en JavaScript mediante cualquiera de las tecnologías que están disponibles en el explorador. Cada objeto gráfico creado en Raphaël también es un objeto DOM, con todas las capacidades de las que disfrutan los objetos DOM, como controladores de eventos de enlace y acceso jQuery. Raphaël también tiene un sistema de animación que permite definir animaciones independientes de los objetos dibujados, lo cual permite una gran reutilización:
var raphael = Raphael(0, 0, 800, 600);
var circle = raphael.circle(100, 100, 200);
circle.attr("fill", "red");
circle.animate({r: 600}, 3000);
// Or make a custom animation
var myAnimation = Raphael.animation(
{r: 600},
3000);
circle.animate(myAnimation);
En este código, en lugar de dibujar un círculo, Raphaël colocará un documento SVG en la página con un elemento de círculo. Curiosamente, Raphaël no admite de forma nativa la carga de archivos SVG. Raphaël tiene una comunidad enriquecida, por lo que hay un complemento para ello disponible en bit.ly/1AX9n7q.
Snap.svg Snap.svg se parece bastante a Raphaël:
var snap = Snap("#myCanvas"); // Add an SVG area to the myCanvas element
var circle = snap.circle(100, 100, 200);
circle.attr("fill", "#FF0000");
circle.animate({r: 600}, 1000);
Una de las principales diferencias es que Snap.svg incluye la importación sin problemas de SVG:
Snap.load("myAwesomeSVG.svg");
La segunda diferencia clave es que Snap.svg proporciona eficaces herramientas integradas para buscar y editar la estructura de SVG implementada, si conoce la estructura del SVG con el que está trabajando. Por ejemplo, imagine que desea que todos los grupos (etiquetas "g") de su SVG sean invisibles. Cuando se cargue SVG, debe agregar esta funcionalidad en una devolución de llamada al método load:
Snap.load("myAwesomeSVG.svg", function(mySVG) {
mySVG.select("g").attr("opacity", 0);
});
El método select funciona de manera muy similar al selector de "$" de jQuery y es bastante eficaz. Desproteja Snap.svg en snapsvg.io.
Un poco más: p5.js
Muchas de estas bibliotecas proporcionan algo adicional para tareas comunes. Esto crea una serie de tecnologías para tratar una amplia gama de aplicaciones, desde un dibujo sencillo hasta medios interactivos y complejas experiencias de juego. ¿Qué más hay ahí fuera en mitad de la serie, algo más que simples soluciones de dibujo, pero no un motor de juego lo bastante completo?
Un proyecto que vale la pena destacar es p5.js, que se crea a partir del lenguaje de programación de procesamiento popular (vea processing.org). Esta biblioteca de JavaScript proporciona un entorno de medios interactivos mediante la implementación del procesamiento en el explorador. p5.js consolida las tareas más comunes en un conjunto de funciones que debe definir para responder a eventos en el sistema, como el nuevo dibujo de la escena o la entrada del mouse. Es muy similar a Paper.js, pero también con bibliotecas multimedia. Este es un ejemplo, que muestra la manera en que este enfoque genera un código de gráficos más conciso:
float size = 20;
function setup() {
createCanvas(600, 600);
}
function draw() {
ellipse(300, 300, size, size);
size = size + .1;
}
Este programa hace un círculo que aumenta de tamaño hasta rellenar la pantalla. Desproteja p5.js en p5js.org.
Por tanto, ¿qué se debe usar?
Claramente, hay ventajas e inconvenientes al lienzo y al SVG. También hay bibliotecas que reducen considerablemente muchas de las desventajas de cualquiera de estos dos enfoques. Por tanto, ¿qué debe usar? Generalmente no recomiendo usar HTML vainilla. Lo más probable es que un juego moderno superare la complejidad gráfica que puede admitir. Así que eso nos lleva a una elección entre SVG y el lienzo, lo cual es difícil.
Para géneros de juegos muy distintivos, la respuesta es algo más sencilla. Si está creando un juego con cientos de miles de partículas, querrá usar el lienzo. Si va a crear un juego de aventuras de apuntar y hacer clic con un estilo de cómic, conviene tener en cuenta SVG.
Para la mayoría de los juegos, no es cuestión del rendimiento, como a muchos les gustaría hacerle creer. Puede pasar horas profundizando en qué biblioteca usar. Sin embargo, al final, ese tiempo podría haberlo invertido en crear el juego.
Mi recomendación es realizar una selección en función de sus activos de material gráfico. Si está realizando las animaciones de personajes en Adobe Illustrator o Inkscape, ¿por qué convertir cada fotograma de las animaciones en píxeles? Utilice el arte vectorial de forma nativa. No desperdicie todo ese trabajo metiendo material gráfico en el lienzo.
A la inversa, si el arte se basa principalmente en píxeles, o va a generar efectos complejos píxel a píxel, el lienzo es una opción perfecta.
Una opción más
Si desea obtener el mejor rendimiento posible, y está dispuesto a tratar con un poco más de complejidad para ponerlo en práctica, es muy recomendable que tenga en cuenta Pixi.js. A diferencia de cualquier otra cosa que le he mostrado en este artículo, Pixi.js utiliza WebGL para la representación 2D. Esto proporciona algunas mejoras importantes de rendimiento.
La API no es tan directa como las demás descritas aquí, pero no es una gran diferencia. Además, WebGL no es compatible con muchos exploradores como las demás tecnologías. Por tanto, en sistemas más antiguos Pixi.js no tiene ninguna ventaja de rendimiento. En cualquier caso, haga su elección y disfrute del proceso.
Michael Oneppo es un especialista en tecnología creativa que fue jefe de programas en el equipo de Direct3D de Microsoft. Entre sus esfuerzos recientes se incluye su trabajo como director de tecnología en la tecnología sin ánimo de lucro Library For All y la investigación en un máster en el NYU Interactive Telecommunications Program.
Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: Shai Hinitz