En este artículo vamos a explicar cómo crear nuestra propia librería gráfica con JavaScript y la API HTML5 de Canvas. A lo largo de diferentes artículos ya hemos explicado en que consiste la etiqueta o tag canvas de HTML5, la cual nos permite dibujar gráficos en la web. También hemos visto cómo dibujar diferentes formas gráficas básicas, mostrar imágenes o cargar recursos gráficos en nuestra sección videojuegos.
Ahora es el momento de crear una librería gráfica en JavaScript, que agrupe todas estas funciones en un único archivo y que pueda ser llamada y ejecutada en cualquier parte de nuestros videojuegos. Esto nos permitirá tener una herramienta flexible y potente como motor de nuestros videojuegos. A la hora de crear nuestra librería gráfica JavaScript hay varias cosas a tener en consideración:
- Qué uso le vamos a dar: Al crear una librería hay que definir para qué la creamos. En este caso vamos a crear una librería gráfica para usarla en nuestros videojuegos, evitando añadir funciones extra o inútiles.
- Qué tipo de funcionalidades vamos a crear: Será la encargada de cargar y/o crear todos los elementos gráficos de nuestros videojuegos. También podrá operar a bajo nivel con estos elementos gráficos permitiendo rotaciones, comprobar colisiones, escalar elementos gráficos, etc. Resumiendo, debe ser el motor gráfico de nuestros videojuegos.
- Qué tipo de normas o estándares vamos a doptar: En nuestro caso, la librería debe ser rápida y optimizada, omitiendo código innecesario o redundante para ofrecer la máxima tasa de fps.
Creando una librería gráfica JavaScript
Hay varias formas de crear una librería gráfica con JavaScript y en Internet se puede ver muchas formas de hacerlo. En nuestro caso vamos a crearla de manera que ofrezca el mayor rendimiento y podamos asignarla a un lienzo <canvas>
como en el siguiente ejemplo:
<canvas id="miCanvas" class="canvas" width="800" height="450"></canvas>
<script>
let libreria = new canvasAPI("miCanvas");
</script>
En este ejemplo creamos una instancia de (canvasAPI) que es el nombre de nuestra librería gráfica, y la asociamos a nuestro lienzo <canvas>
mediante su id (miCanvas). Con este método, podemos tener varias instancias de la librería sobre un mismo lienzo o incluso tener varios <canvas>
y cada uno con su propia librería. Es una forma fácil, dinámica y flexible de crear una librería gráfica con JavaScript que permite muchas variaciones.
Una vez creada la instancia de nuestra librería gráfica, podemos dibujar en el lienzo «miCanvas» a través de la variable «libreria» de JavaScript y usar las funciones que programemos en ella. Veamos cómo es el código de nuestra librería gráfica a nivel interno.
Estructura de la librería gráfica
Nuestra librería gráfica JavaScript será básicamente una función encapsulada, ideal para proteger su lógica interna y ofrecer mejor rendimiento que una clase/objeto típicos. Consta de tres grandes partes:
- Propiedades: Donde definimos todas las variables que nuestra librería necesita para funcionar.
- Funciones: Donde añadimos todas las funciones que nuestra librería necesita para funcionar.
- Interfaz: Donde hacemos públicas y accesibles desde el exterior, todas las propiedades y funciones de la librería que podrán ser utilizadas en nuestros videojuegos.
Veamos un pequeño ejemplo práctico y funcional:
let canvasAPI = function(canvasID) {
let canvas = document.getElementById(canvasID);
let ctx = canvas.getContext("2d");
function clearScreen() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function circle(data) {
Object.assign(this, data);
}
circle.prototype.draw = function() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x + this.radius, this.y + this.radius, this.radius, 0, 2 * Math.PI);
ctx.fill();
}
return {
width: canvas.width,
height: canvas.height,
clearScreen: clearScreen,
circle: circle,
}
}
Código de la librería gráfica
Nuestra librería gráfica JavaScript se define dentro de una función llamada (canvasAPI
) que actúa como una encapsulación, protegiendo todos sus elementos del exterior. Al ser llamada con el ID de un <canvas>
, inicializa el contexto gráfico (canvas
, ctx
) y devuelve un conjunto de funciones y propiedades (return) que se pueden utilizar para crear y dibujar elementos gráficos. Esto permite tener múltiples instancias independientes si se usan varios <canvas>
en una misma página. Según tu nivel de JavaScript y programación de videojuegos, puedes leer alguno de mis libros para profundizar más sobre estos temas.
clearScreen()
Se trata de una función básica pero esencial para cualquier motor gráfico. Limpia completamente el lienzo <canvas>
en cada fotograma, evitando que los dibujos anteriores queden superpuestos. Esto permite una animación fluida y controlada fotograma a fotograma. Puedes leer los artículos FPS en videojuegos JavaScript y Delta Time en Videojuegos para más información.
circle() y su método draw()
En este ejemplo, nuestra librería gráfica JavaScript incluye un único tipo de elemento gráfico (circle
), que representa un círculo relleno en el <canvas>
. Este círculo se crea mediante una función constructora circle(data)
, que utiliza Object.assign(this, data)
para asignar al objeto todas las propiedades que reciba como parámetro. De este modo, podemos inicializar cada círculo con los valores que necesitemos sin tener que declararlos uno a uno en el constructor.
Al crear un círculo con el operador new circle({..})
, obtenemos un objeto independiente con sus propias propiedades, al que se le ha añadido un método draw()
en su prototipo. Este método se encarga de pintar el círculo en el lienzo <canvas>
, utilizando las propiedades almacenadas en (this
) como posición, color o radio.
Esta estructura de nuestra librería gráfica JavaScript, es especialmente útil cuando queremos crear objetos gráficos de forma dinámica, sin saber de antemano qué propiedades necesitaremos. Por ejemplo, además de las propiedades gráficas básicas, podemos incluir otras como vida, velocidad, dirección, tipo, etc. que pueden ser útiles en videojuegos.
Gracias a este enfoque, cada círculo creado funciona como una entidad autónoma que puede moverse, cambiar de color o colisionar sin afectar a los demás. Esta misma idea puede aplicarse para crear otros elementos gráficos, como rectángulos, imágenes, tiles o sprites, todos con comportamiento independiente. Lo que otorga a nuestra librería gráfica JavaScript todo el potencial que necesitamos para el desarrollo de videojuegos. Puedes leer el artículo círculos en HTML5 Canvas para más información.
Utilizando la librería gráfica
Ahora que hemos visto el código JavaScript de nuestra librería gráfica, vamos a ver cómo podemos utilizarla en nuestros videojuegos con un ejemplo funcional.
// USO DE LA LIBRERÍA
let libreria = new canvasAPI("miCanvas");
let items = []; // Lista de elementos gráficos
// Creamos varios círculos con datos aleatorios
for (let i=0; i<10; i++) {
let radius = 25 + Math.floor(Math.random() * 25);
let circle = new libreria.circle({
x: libreria.width/2,
y: libreria.height/2,
radius: radius,
width: radius * 2,
height: radius * 2,
color: "#" + Math.random().toString(16).substr(-6),
speedX: 5 - Math.floor(Math.random() * 10),
speedY: 5 - Math.floor(Math.random() * 10),
});
items.push(circle);
}
// Salta al bucle de animación
animateScene();
// Bucle de animación
function animateScene() {
libreria.clearScreen();
for (let i=0; i<items.length; i++) items[i].draw();
for (let i=0; i<items.length; i++) {
let item = items[i];
item.x += item.speedX;
item.y += item.speedY;
if (item.x < 0 || (item.x + item.width) > libreria.width) item.speedX = -item.speedX;
if (item.y < 0 || (item.y + item.height) > libreria.height) item.speedY = -item.speedY;
}
requestAnimationFrame(animateScene);
}
Creación de objetos gráficos
En este ejemplo, iniciamos nuestra librería gráfica JavaScript con:
let libreria = new canvasAPI("miCanvas")
A continuación, creamos diez círculos con propiedades aleatorias (posición, tamaño, color y velocidad), y los almacenamos en el array (items
). Para ello usamos el constructor libreria.circle()
, que nos permite inicializar cada objeto con las propiedades necesarias sin restricciones de formato ni orden. Gracias a esta estructura, cada círculo es una entidad independiente, con sus propios datos y comportamiento, ideal para videojuegos, simulaciones o visualizaciones interactivas.
Bucle de animación
Después de crear los elementos, entramos en la función animateScene()
, que actúa como bucle de animación. Este bucle es el motor del juego: se ejecuta de forma continua y se encarga de actualizar la lógica del programa y redibujar la escena en cada fotograma. Puedes leer el artículo bucle de animación de videojuegos para más información.
En cada iteración del bucle se realizan tres pasos:
- Borrado del lienzo: antes de dibujar un nuevo fotograma, se limpia el canvas para evitar trazas de los elementos anteriores.
- Dibujo de elementos: se recorren todos los elementos en (
items
) y se llama a su métododraw()
para renderizarlos con sus propiedades actuales. - Actualización de lógica: se actualiza la posición de cada objeto en base a su velocidad y se comprueba si colisiona con los bordes del canvas. Si es así, se invierte su dirección para simular un rebote.
El uso de requestAnimationFrame
permite que la animación sea fluida, adaptándose automáticamente a la frecuencia de refresco del navegador y del dispositivo.
Librería gráfica con JavaScript
Este ejemplo demuestra lo sencillo y eficaz que puede ser construir una librería gráfica modular y reutilizable utilizando JavaScript puro. Su estructura basada en funciones constructoras con métodos de dibujo independientes resulta ideal para desarrollar videojuegos, simulaciones o cualquier tipo de visualización interactiva.
El patrón de diseño empleado permite extender fácilmente nuestra librería gráfica JavaScript para incorporar nuevos tipos de objetos gráficos, como imágenes, sprites, texto, tiles o mapas completos, manteniendo siempre la compatibilidad con los elementos ya creados.
Este enfoque de librería gráfica en JavaScript es el que utilizo en mis videojuegos y el que explico en mis libros, incluyendo la creación de Mapas y Mundos 2D pero manteniendo la compatibilidad con todo lo explicado anteriormente.
Se trata de una herramienta potente, flexible y perfectamente adaptada para convertirse en el motor gráfico de cualquier videojuego desarrollado con JavaScript puro.
¡ Espero que este artículo sea de vuestro interés !