Advertisement
  1. Game Development
  2. Phaser
Gamedevelopment

Una Introducción Actualizada para Crear Mundos Isométricos, Parte 1

by
Difficulty:BeginnerLength:LongLanguages:
This post is part of a series called Primer for Creating Isometric Worlds.
Updated Primer for Creating Isometric Worlds, Part 2

Spanish (Español) translation by Javier Salesi (you can also view the original English article)

Final product image
What You'll Be Creating

Todos nos hemos entretenido suficientes veces con los fabulosos juegos isométricos, ya se el original Diablo, o Era de los Imperios o Comandos. La primera vez que te topaste con un juego isométrico, te podrías haber preguntado si era un juego 2D o un juego 3D o algo completamente diferente. El mundo de los juegos isométricos también tiene su mística atracción para los desarrolladores de juegos. Permítenos revelar el misterio de la proyección isométrica y tratar de crear un sencillo mundo isométrico en éste tutorial.

Éste tutorial es una versión actualizada de mi tutorial sobre la creación de mundos isométricos. El tutorial original utilizaba Flash con ActionScript y aún es relevante para desarrolladores en Flash u OpenFL. En éste nuevo tutorial he decidido usar Phaser con código JS, de éste modo creando un resultado de HTML5 interactivo en lugar de un resultado SWF.

Por favor toma en cuenta que éste no es un tutorial sobre desarrollar en Phaser, sino solo estamos usando Phaser ara comunicar fácilmente los conceptos esenciales de la creación de una escena isométrica. Además, hay maneras mucho mejores y más fáciles de crear contenido isométerico en Phaser, como el Plugin Phaser Isometric.

Por simplicidad, usaremos el planteamiento basado en azulejos para crear nuestra escena isométrica.

1. Juegos Basados en Azulejos

En juegos 2D usando el planteamiento basado en azulejos, cada elemento visual se divide en piezas más pequeñas, llamadas azulejos, de un tamaño estándar. Éstos azulejos serán organizados para formar el mundo del juego de acuerdo a datos de nivel pre-determinado-generalmente un arreglo bidimensional.

Posts Relacionados

Generalmente los juegos basados en azulejos usan una vista arriba-abajo o una vista lateral para la escena del juego. Permítenos considerar una vista estándar 2D arriba-abajo con dos azulejos-un azulejo césped y un azulejo pared-como se muestra aquí:

Green and Maroon tiles

Los dos azulejos son imágenes cuadradas del mismo tamaño, por lo tanto la altura del azulejo y la anchura del azulejo es la misma. Déjanos considerar un nivel de juego que es una pradera delimitada en todos los costados por paredes. En tal caso, los datos de nivel representados con un arreglo bidimensional se ven así:

Aquí, 0 denota un azulejo de césped y 1 denota un azulejo de pared. Organizar los azulejos de acuerdo a datos de nivel producirá nuestra pradera amurallada como se muestra en la imagen de abajo:

Top view level - grass area surrounded by walls

Podemos ir un poco más lejos al agregar azulejos de esquina y separar los azulejos de pared vertical y horizontal, requiriendo cinco azulejos adicionales, que nos conducen a nuestros datos de nivel actualizados:

Consulta la imagen de abajo, donde he marcado los azulejos con sus correspondientes números de azulejo en los datos de nivel:

Better top view level with corner tiles and tile numbers

Ahora que hemos entendido el concepto del planteamiento basado en el azulejo, permíteme mostrarte cómo podemos usar un sencillo pseudo código de cuadrícula 2D para renderizar nuestro nivel:

Si usamos las imágenes de azulejos de arriba entonces la anchura del azulejo y la altura del azulejo son iguales (y las mismas para todos los azulejos), y coincidirá con las dimensiones de la imágenes de los azulejos. Así que la anchura del azulejo y la altura del azulejo para éste ejemplo son de 50 px, que conforma el tamaño del nivel total de 300 x 300 px-esto es, seis filas y seis columnas de azulejos que miden 50 x 50 px cada uno.

Como se discutió anteriormente, en un planteamiento normal basado en azulejos, implementamos una vista arriba-abajo o una vista lateral; para una vista isométrica necesitamos implementar la proyección isométrica.

2. Proyección Isométrica

La mejor explicación técnica de lo que significa proyección isométrica, por lo que a mí respecta, es de éste artículo de Clint Bellanger:

Ponemos en ángulo nuestra cámara junto con dos ejes (girando la cámara 45 grados a un lado, luego 30 grados hacia abajo). Ésto crea una cuadrícula en forma de diamante (rombo), donde los espacios de la cuadrícula en la que el ancho es dos veces la dimensión de la altura. Éste estilo fue popularizado por juegos de estrategia y acción RPGs. Si miramos un cubo en ésta vista, tres lados son visibles (superior y dos laterales).

Aunque suena un poco complicado, implementar ésta vista es realmente muy fácil. Lo que necesitamos comprender es la relación entre espacio 2D y el espacio isométrico-esto es, la relación entre lo datos de nivel y la vista; la transformación de las coordenadas Cartesianas arriba-abajo a coordenadas isométricas. La imagen de abajo muestra la transformación visual:

Side by side view of top down and isometric grids

Colocando Azulejos Isométricos

Déjame tratar de simplificar la relación entre los datos de nivel almacenados como un arreglo 2D y la vista isométrica-ésto es, cómo transformarmos las coordenadas Cartesianas en coordenadas isométricas. Trataremos de crear la vista isométrica para nuestra ahora famosa pradera amurallada. La implementación de la vista 2D del nivel fue una sencilla iteración con dos bucles, colocando azulejos cuadrados desplazando cada uno con los valores fijos de la altura del azulejo y la anchura del azulejo. Para la vista isométrica, el pseudo código sigue siendo el mismo, pero la función placeTile() cambia.

La función orignial solo dibuja las imágenes del azulejo en las coordenadas proporcionadas x y y, pero para una vista isométrica necesitamos calcular las correspondientes coordenadas isométricas. Las ecuaciones para hacer ésto son como sigue, donde isoX e isoY representan coordenadas isométricas x- y y-, y cartX y cartY representan coordenadas Cartesianas x- y y-:

Si, eso es. Éstas simples ecuaciones es la magia detrás de la proyección isométrica. Aquí están las funciones helper de Phaser que pueden ser usadas para convertir de un sistema a otro usando la muy conveniente clase Point:

Así que podemos usar el método helper cartesianToIsometric para convertir las coordenadas 2D entrantes en coordenadas isométricas dentro del método placeTile. Aparte de ésto, el código de renderizado sigue siendo el mismo, pero necesitamos tener nuevas imágenes para los azulejos. No podemos usar los viejos azulejos cuadrados para nuestro renderizado arriba-abajo. La imagen de abajo muestra los nuevos azulejos isométricos de césped y pared junto con el nivel isométrico renderizado:

Isometric level walled grassland along with the isometric tiles used

Increíble, ¿no? Veamos cómo una típica posición 2D se convierte en una posición isométrica:

Similarmente, una entrada de [0, 0] resultará en [0, 0], y [10, 5] dará [5, 7.5].

Para nuestro césped amurallado, podemos determinar un área transitable al verificar si el elemento del arreglo es 0 en esa coordenada, por lo tanto indicando césped. Para ésto necesitamos determinar las coordenadas del arreglo. Podemos encontrar las coordenadas del azulejo en los datos de nivel desde sus coordenadas Cartesianas usando ésta función:

(Aquí, esencialmente asumimos que la altura del azulejo y la anchura del azulejo son iguales, como en la mayoría de los casos.)

Por lo tanto, desde un par de coordenadas de pantalla (isométricas), podemos encontrar las coordenadas del azulejo al llamar:

Éste punto de la pantalla podría ser, digamos, la posición de un click del mouse o una posición de recoger un elemento.

Puntos de Registro

En Flash, podríamos establecer puntos arbitrarios para un gráfico como su punto central o [0,0]. El equivalente en Phaser es Pivot. Cuando colocas el gráfico en digamos [10,20], entonces éste punto Pivot se alineará con [10,20]. Por defecto, la esquina superior izquierda de un gráfico es considerada su [0,0] o Pivot. Si tratas de crear el nivel de arriba usando el código proporcionado, entonces no obtendrás el resultado desplegado. En cambio, obtendrás una tierra plana sin las paredes, como la de abajo:

The issue with wall tiles when rendered normally

Ésto es porque las imágenes del azulejo son de tamaños diferentes y no vamos a abordar el atributo de altura del azulejo de la pared. La imagen de abajo muestra las diferentes imágenes de azulejo que usamos con sus cuadros delimitadores y un círculo blanco donde su predeterminado [0,0] es:

How to properly align the different tiles along with their registration points

Ve cómo el héroe está mal alineado cuando se dibuja usando los pivots predeterminados. También nota cómo perdimos la altura del azulejo de la pared si se dibuja usando los pivots predeterminados. La imagen de la derecha muestra cómo necesitan estar adecuadamente alineados para que el azulejo de la pared obtenga su altura y el héroe sea colocado en el centro del azulejo del césped. Éste problema puede resolverse de diferentes maneras.

  1. Haz todos los azulejos del tamaño de la misma imagen con la gráfica alineada adecuadamente dentro de la imagen. Ésto crea muchas áreas vacías dentro de cada gráfico del azulejo.
  2. Establece los puntos pivot manualmente para cada azulejo para que se alinien adecuadamente.
  3. Dibuja azulejos con desplazamientos específicos para que se alinien adecuadamente.

Para éste tutorial, he elegido utilizar el tercer método para que ésto funcione aún con un framework sin la capacidad de establecer puntos pivots.

3. Moviéndose en Coordenadas Isométricas

Nunca trataremos de mover nuestro personaje o proyectil en coordenadas isométricas directamente. En cambio, manipularemos nuestros datos del mundo de juego en coordenadas Cartesianas y solo usaremos las funciones de arriba para actualizar esos en la pantalla. Por ejemplo, si quieres mover un personaje hacia adelante en la dirección y- positiva, puedes simplemente incrementar su propiedad y en coordenadas 2D y luego convertir la posición resultante a coordenadas isométricas:

Éste será un buen momento para revisar todos los nuevos conceptos que hemos aprendido hasta ahora y tratar de crear un ejemplo funcionando de algo que se mueva en un mundo isométrico. Puedes encontrar los necesarios recursos de imagen en el directorio assets del repositorio fuente en git.

Ordenamiento en Profundidad

Si trataste de mover la imagen de la bola en nuestro jardín amurallado entonces te toparías con problemas de ordenamiento en profundidad. Además a la colocación normal, necesitaremos tener cuidado del ordenamiento en profundidad para dibujar el mundo isométrico, si hay elementos moviéndose. El correcto ordenamiento en profundidad asegura que los elementos más cercanos a la pantalla sean dibujados arriba de elementos más lejanos.

El método más simple de ordenamiento en profundidad es tan solo usar el valor de la coordenada y- Cartesiana, como se mencionó en éste Consejo Rápido: mientras más alejado está el objeto de la pantalla, más pronto debería ser dibujado. Ésto podría funcionar bien para escenas isométricas muy simples, pero una mejor manera será volver a dibujar una escena isométrica una vez que ocurre un movimiento, de acuerdo a las coordenadas del arreglo del azulejo. Déjame explicar éste concepto en detalle con nuestro pseudo código para dibujo de nivel:

Imagina nuestro elemento o personaje está en el azulejo [1,1]-ésto es, el azulejo verde más arriba en la vista isométrica. Para dibujar correctamente el nivel, el personaje necesita ser dibujado después de dibujar el azulejo de las esquina de la pared, los azulejos de las paredes izquierda y derecha, y el azulejo del terreno, como se muestra abajo:

Hero standing on the corner tile

Si seguimos nuestro bucle de dibujo como por el pseudo código de arriba, dibujaremos primero la pared de la esquina en el centro, y luego continuaremos dibujando todas las paredes en la sección superior derecha hasta que llegue a la esquina derecha.

Luego, en el siguiente bucle, dibujará la pared de la izquierda del personaje, y luego el azulejo del césped en el que el personaje aparece. Una vez que determinamos que éste es el azulejo que ocupa nuestro personaje, dibujaremos el personaje después de dibujar el azulejo del césped.  de ésta manera, si había paredes en los tres azulejos de césped conectados con el que alberga al personaje, esas paredes se superpondrán al personaje, resultando en el adecuado renderizado de ordenamiento en profundidad.

4.Creando el Arte

El arte isométrico puede ser pixel art, pero no tiene que ser. Cuando trabajas con arte isométrico pixelado, la guía de RhysD te dice casi todo lo que necesitas saber. Algo de teoría puede encontrarse en Wikipedia también.

Cuando se crea arte isométrico, las reglas generales son:

  • Comienza con una cuadrícula isométrica en blanco y apégate  a una precisión pixel-perfect.
  • Trata de segmentar el arte en imágenes únicas de azulejo isométrico.
  • Trata de asegurar que cada azulejo sea transitable o no transitable. Será complicado si necesitamos acomodar un solo azulejo que contenga áreas transitables y no transitables.
  • La mayoría de los azulejos necesitarán ajustarse fluídamente en una o más direcciones.
  • Las sombras pueden ser difíciles de implementar, a menos que usemos un planteamiento en capas donde dibujamos sombras en la capa del terreno y luego dibujamos el héroe (o árboles, u otros objetos) en la capa superior. Si el planteamiento que usas no es en capas múltiples, asegúrate que las sombras caigan al frente para que no caigan en digamos, el héroe cuándo está detrás de un árbol.
  • En caso de que necesites utilizar una imagen de azulejo más grande que el tamaño de azulejo isométrico estándar, trata de usar una dimensión que sea un múltiplo del tamaño del azulejo iso. Es mejor tener un planteamiento de organización en capas en tales casos, donde podemos dividir el diseño en diferentes pedazos basados en su altura. Por ejemplo, un árbol puede dividirse en tres piezas: la raíz, el tronco y el follaje. Ésto facilita ordenar profundidades pues podemos dibujar pieza en correspondientes capas que correspondan con sus alturas.

Los azulejos isométricos que son más grandes que las dimensiones de un solo azulejo crearán prolemas con el ordenamiento en profundidad. Algunos de los problemas son discutidos en éstos enlaces:

Posts Relacionados

5. Personajes Isométricos

Primero necesitaremos definir cuántas direcciones de movimiento son permitidas en nuestro juego-generalmente, los juegos proporcionarán movimiento de cuatro direcciones o movimiento de ocho direcciones. Consulta la imagen de abajo para entender la correlación entre espacio 2D y espacio isométrico:

The directions of motion in top view and isometric view

Por favor nota que un personaje estaría moviéndose verticalmente hacia arriba cuando presionamos la tecla flecha arriba en un juego arriba-abajo, pero para un juego isométrico el personaje se moverá en un ángulo de 45 grados hacia la esquina superior derecha.

Para una vista arriba-abajo, podríamos crear un conjunto de animaciones de personaje de frente a una dirección, y simplemente rotándolos para todas las otras. Para diseño de personaje isométrico, necesitamos volver a renderizar cada animación en cada una de las direcciones permitidas-así que para movimiento de ocho direcciones, necesitamos crear ocho animaciones para cada acción.

Para facilitar el entendimiento, generalmente denotamos las direcciones como Norte, Noroeste, Oeste, Suroeste, Sur, Sureste, Este y Noreste. La secuencia del personaje de abajo muestras imágenes fijas que inician desde el Sureste y continúan en dirección de las manecilas del reloj:

The different frames of the character facing the different directions

Colocaremos personajes de la misma manera que colocamos azulejos. El movimiento de un personaje es logrado al calcular el movimiento en coordenadas Cartesianas y luego convirtiendo a coordenadas isométricas. Permítenos asumir que estamos usando el teclado para controlar el personaje.

Definiremos dos variables, dX y dY, basados en las teclas direccionales presionadas. Por defecto, éstas variables serán 0 y serán actualizadas como el gráfico de abajo, donde U, D, R y L denotan las teclas de las flechas Arriba, Abajo, Derecha e Izquierda, respectivamente. Un valor de 1 debajo de una tecla representa que la tecla está siendo presionada; 0 implica que la tecla no está siendo presionada.

Ahora, usando los valores de dX y dY, podemos actualizar las coordenadas Cartesianas así:

Así que dX y dY represenan el cambio en las posiciones x- y y- del personaje, basado en las teclas presionadas. Podemos fácilmente calcular las nuevas coordenadas isométricas, como lo hemos discutido.

Una vez que tengamos la nueva posición isométrica, necesitamos mover el personaje a ésta posición. Basado en los valores que tenemos para dX y dY, podemos decidir hacia que dirección está viendo el persnaje y usar el correspondiente dibujo del personaje. Una vez que el personaje es movido, por favor no olvides volver a pintar el nivel con el adecuado ordenamiento de profundidad pues las coordenadas del azulejo del personaje pudieron haber cambiado.

Detección de Colisión

La detección de colisión se hace al verificar si el azulejo en la posición recientemente calculada es un azulejo no transitable. Así, una vez que encontremos la nueva posición, no movemos inmediatamente el personaje ahí, sino primero verificamos qué azulejo ocupa ese espacio.

En la función isWalkable(), verificamos si el valor del arreglo de los datos de nivel en la coordenada dada es un azulejo transitable o no lo es. Debemos tener cuidado de actualizar la dirección en la que el personaje está volteando-aún si no se mueve, como en el caso de que pase por un azulejo no transitable.

Ahora de ésta manera suena como una solución adecuada, pero sólamente funcionará para elementos sin volumen. Ésto es porque estamos únicamente considerando un solo punto, que es el punto medio del personaje, para calcular colisión. Lo que realmente necesitamos hacer es encontrar las cuatro esquinas del personaje desde su coordenada de medio punto 2D disponible y calcular colisiones para todas esas. Si cualquier esquina está cayendo dentro de un azulejo no transitable, entonces no deberíamos mover el personaje.

Ordenamiento en Profundidad con Personajes

Considera un azulejo de personaje y de un árbol en el mundo isométrico, y ambos tienen el mismo tamaño de imágenes, no importa que suene irreal.

Para entender correctamente el ordenamiento en profundidad, debemos entender que cuando las coordenadas x- y y- del personaje son menores que las del árbol, el árbol se superpone al personaje. Cuando las coordenadas x- y y- del personaje son mayores que las del árbol, el personaje se superpone al árbol. Cuando tienen la misma coordenada x-, entonces decidimos basados nada más en la coordenada y-: cualquiera tenga la coordenada y- más alta se superpone al otro. Cuando tienen la misma coordenada y- entonces decidimos basados nada más en la coordenada x-: cualquiera que tenga la coordenada -x más alta se superpone al otro.

Como se explicó antes, una versión simplificada de ésto es para sólamente dibujar secuencialmente los niveles comenzando desde el azulejo más alejado-esto es, tile[0][0]-y luego dibujar todos los azulejos en cada fila uno por uno. Si un personaje ocupa un azulejo, dibujamos primero el azulejo del piso y luego renderizamos el azulejo del personaje. Ésto funcionará bien, porque el personaje no puede ocupar un azulejo de pared.

6. ¡Tiempo de una Demostración!

Éste es un demo en Phaser. Haz click para enfocarte en el área interactiva y usa tus teclas de flechas para mover el personaje. Podrías usar dos teclas de flecha para moverlo en direcciones diagonales.

Puedes encontrar el código fuente completo para el demo en el repositorio fuente para éste tutorial. El código JS principal se proporciona abajo:

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.