`

Como crear tu videojuego desde 0 en la web con HTML5 y JavaScript - Capítulo 1

Buenas!

Llegamos, primer capítulo de la super serie de creación de videojuegos con HTML5 y JavaScript, como dijimos en el blog de presentación, si no lo leyeron pueden ir a verlo acá (Presentación del blog), esta va a ser la primer serie de muchos tutoriales que se vienen!

El resultado final de esta serie va a ser algo así:

Antes de empezar, y como dije en el blog anterior, todo contenido va a ser 100% gratuito, con lo único que me ayudarían es dándome un follow en Twitter: @DamianCatanzaro  y compartiendo el contenido para que más gente lo pueda usar 😁.

Ahora si, empecemos!

<canvas> una etiqueta que nace con HTML5 y nos permite tanto dibujar como renderizar imágenes dentro, todo nuestro juego va a pasar adentro de una o varias etiquetas canvas, después les voy a explicar porque quisiéramos usar varias.

See the Pen Creación de VideoJuegos - Capitulo 1 - CodePen 1 by Damián Catanzaro (@dcatanzaro) on CodePen.

Como vemos, arrancamos con una etiqueta <canvas> en el html con un ID en la misma y desde JavaScript iniciamos ese canvas con 2 métodos

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

Agarramos el elemento canvas con la ID que le pusimos, en este caso id="canvas" y después usamos la función "getContext", esta función nos va a dar el contexto de este canvas con el cual podemos escribir texto, dibujar círculos, lineas y la más importante que vamos a estar usando a lo largo de todo el tutorial, poder renderizar imágenes.

const image = new Image();

//Url de la imagen
image.src = "https://argentumonlineweb.com/static/graficos/3013.png";

image.onload = function() {
    //context.drawImage(instancia de la imagen, x, y);
    context.drawImage(image, 100, 100);
};

Ya sabemos renderizar imágenes, ahora avancemos un poco más, necesitamos renderizar el suelo de nuestro juego. Al ser un juego 2D y por tiles, nuestro suelo va a ser una cuadricula en la que cada tile va a tener 32px x 32px y cada uno de esos tiles va a tener esta imagen:

Vamos a empezar haciendo un mapa simple, nuestro mapa va a tener 10 tiles de largo por 10 tiles de ancho, si hacemos el calculo nos quedaría un canvas de 32px x 10 tiles = 320px, así que ese va a ser el tamaño de nuestro nuevo canvas.

See the Pen Creación de VideoJuegos - Capitulo 1 - CodePen 2 by Damián Catanzaro (@dcatanzaro) on CodePen.

Qué hicimos? Como primera medida declaramos las constantes de nuestro mapa

const mapSize = {
    x: 10,
    y: 10
};

const sizeTile = 32;

10 tiles para el eje-X, 10 tiles para el eje-Y y 32px tanto de ancho como de largo para cada tile.

Hora de crear nuestro mapa, para eso vamos a tener que usar 2 bucles diferentes, uno que recorra todos los tiles del eje-Y y otro que recorra todos los tiles del eje-X para así pasar por todos los tiles y en cada uno de los tiles dejar dibujada la imagen que vimos anteriormente.

Si analizamos la segunda parte del código vamos a ver como los 2 bucles van armando toda la cuadrilla de tiles. Si se acuerdan anteriormente la función drawImage nos permite renderizar en el canvas una imagen, la misma pide como parámetros la imagen, el eje-X y el eje-Y desde donde se va a empezar a dibujar.

const image = new Image();
image.src = 'https://i.imgur.com/fqG34pO.png'; //Incluimos la imagen

image.onload = function () {
    for (let y = 0; y <= mapSize.y; y++) {
        for (let x = 0; x <= mapSize.x; x++) {
            context.drawImage(image, x * sizeTile, y * sizeTile);
        }
    }
};

Entonces la primera pasada tenemos y = 0, x = 0

context.drawImage(image, 0 * 32, 0 * 32);

Segunda, y = 0, x = 1

context.drawImage(image, 1 * 32, 0 * 32);

Y así hasta que nuestros 2 for terminen su ejecución y hayan dibujado todo el mapa.

Si lo vemos en cámara lenta, el código lo que va a hacer es lo siguiente:

Mapa creado, ahora quiero tener un personaje arriba del mapa el cual va a poder caminar por el mundo.

Así que hacemos lo mismo, con context.drawImage lo ponemos en cualquier parte del mapa, yo lo voy a centrar en el medio.

See the Pen Creación de VideoJuegos - Capítulo 1 - CodePen 3 by Damián Catanzaro (@dcatanzaro) on CodePen.

Bien, que pasó acá? Se descontroló un poco todo, no se preocupen, es lo mismo que teníamos antes pero ordenado en funciones y con un par de características extras, pasemos a explicar.

La primera función que vemos es loadImage, vamos a tener que pasar a cargar mucha cantidad de imágenes, así que es mejor concentrar toda esa lógica en una función.

function loadImage(src) {
    return new Promise((resolve, reject) => {
        const image = new Image();
        image.src = src;

        image.onload = () => {
            resolve(image);
        };
        image.onerror = reject;
    });
}

Por si no conocen las Promises en JavaScript, les paso a explicar muy por arriba como funcionan y que hacen y les dejo un link abajo para que las puedan investigar más a fondo, una Promise o Promesa en JavaScript lo que hace es esperar un "resolve" o "reject", al descargar la imagen no sabemos cuanto puede llegar a tardar la descarga por lo que toda esta función pasa a ser una función asíncrona, con esta Promise, vamos a ver en el código de más adelante, que nos va a permitir encolar la imágenes e irlas cargando secuencialmente, porque queremos esto ahora? Imagínense que se carga primero el personaje y después el pasto, se renderizarían en orden diferentes y el pasto quedaría pisando al personaje.

Segunda parte, función renderMap

async function renderMap() {
    const imageTile = await loadImage(urlTile);
    const imageCharacter = await loadImage(urlCharacter);

    for (let y = 0; y <= mapSize.y; y++) {
        for (let x = 0; x <= mapSize.x; x++) {
            context.drawImage(imageTile, x * sizeTile, y * sizeTile);
        }
    }

    context.drawImage(imageCharacter, 100, 100);
}

Acá encontramos 2 propiedades de JavaScript que funcionan en conjunto con las Promises, que son "async" y "await", de esto también voy a dejar material abajo de todo por si lo quieren investigar, pero lo que hace await es esperar a que la Promise se resuelva y recién ahí continuar el código.

const imageTile = await loadImage(urlTile);
const imageCharacter = await loadImage(urlCharacter);

En este caso, esperamos que se descargue la imagen del tile y después descargamos la imagen del personaje.

for (let y = 0; y <= mapSize.y; y++) {
    for (let x = 0; x <= mapSize.x; x++) {
        context.drawImage(imageTile, x * sizeTile, y * sizeTile);
    }
}

context.drawImage(imageCharacter, 100, 100);

Una vez que tenemos ambos descargados pasamos por los 2 for, tanto en el eje-X como el eje-Y para dibujar el pasto y después dibujamos el personaje en la posición X: 100 Y: 100.

Hasta acá ya sabemos como funciona canvas, como descargar una imagen, como renderizarla y como armar un mapa.

El primer capitulo lo voy a cortar acá porque es muchísima información para procesar, aprender y practicar, de verdad, practiquen, pueden hacerlo en un CodePen como los que subí yo, es híper fácil de usar.

Siempre al cerrar un capítulo voy a dejar un challenge para que hagan con el código que tenemos actualmente, es una buena manera que se pongan a tocar el código y entender realmente como funciona más allá de la teoría.

El challenge que toca ahora es agregar estas dos imágenes al canvas, pueden estar por cualquier lado y al cartel escribirle texto adentro.

Acá les dejo ambas urls:

Cartel: https://i.imgur.com/NXIjxr8.png

Árbol: https://i.imgur.com/wIK2b9P.png

Material de lectura:

Promise: link

Async / Await: link

El próximo capitulo vamos a arrancar mostrando como se resuelve el challenge pero antes inténtenlo ustedes! Practicando es la única manera de aprender.

Si pudieron resolverlo o hacer alguna otra cosa diferente háganmelo saber por Twitter y cualquier duda, consulta o sugerencia que tengan lo mismo! @DamianCatanzaro

Y como último, también se pueden suscribir al blog o seguirme en Twitter para recibir todas las notificaciones de cuando suba nuevos capítulos!

Nos vemos en el siguiente capítulo!

Follow @DamianCatanzaro