jueves, 1 de mayo de 2014

Isometric engine

Los videojuegos en perspectiva isométrica tuvieron su auge en la era de los años ochenta con títulos como "Knight Lore"1, "Head over Hells" o "La Abadía del Crimen". Con esta técnica se consiguió crear sensación de tridimensionalidad en plataformas 8-bits donde aún no era posible soportar complejos cálculos geométricos debido a su poca memoria y velocidad de procesador. En esta perspectiva, los ejes (x, y, z) guardan una relación equidistante de 120º, de ahí su denominación.

Hoy comenzamos la explicación de esta técnica de representación gráfica introduciendo los conceptos clave detallados en el tutorial de Juwal Bose en la Web de creadores multimedia de tuts+. En líneas generales, la construcción de un motor en perspectiva isométrica consiste básicamente en mantener una estructura de datos en forma de matriz donde almacenar los identificadores del tipo de objeto ("tile") en pantalla. Por ejemplo, si suponemos la siguiente pantalla de un nivel de un juego en dicha representación:

Pantalla de Spindizzy (emulado con CPCBox)

 y si consideramos los valores de 0 a 10 para los diferentes "tiles":
  • 0 Suelo
  • 1 Muro
  • 2,3 Rampa A
  • 4,5 Rampa B
  • 6 Diamante
  • 7 Flecha Norte
  • 8 Flecha Sur
  • 9 Flecha Oeste
  • 10 Flecha Este 
  • -1 Nada
Obtendríamos un array 2D para la pantalla anterior con la siguiente estructura:

Capa 1:
 [[1,1,1,4,5,1,1,1],
 [1,6,0,9,9,0,0,1],
 [1,0,0,0,0,0,0,1],
 [2,8,0,0,0,0,7,2],
 [3,8,0,0,0,0,7,3],
 [1,0,0,0,0,0,0,1],
 [1,0,0,0,10,0,0,1],
 [1,0,0,0,10,0,0,1]]
Capa 2:
 [[-1,-1,-1,-1,-1,-1,-1,6],
 [-1,-1,-1,-1,-1,-1,-1,-1],
 [-1,-1,-1,-1,-1,-1,-1,-1],
 [-1,-1,-1,-1,-1,-1,-1,-1],
 [-1,-1,-1,-1,-1,-1,-1,-1],
 [-1,-1,-1,-1,-1,-1,-1,-1],
 [-1,-1,-1,-1,-1,-1,-1,-1],
 [-1,-1,-1,-1,-1,-1,-1,-1]]
Podríamos representar una pantalla con la siguiente rutina:
for (i = 0 hasta total filas)
 for (j = 0 hasta total columnas)
  x = j * ancho tile 
  y = i * alto tile 
  tipoTile = levelData[i][j]
  posicionaTile(tipoTile, x, y)
Es posible localizar un punto cartesiano en la perspectiva isométrica mediante la función:

//Conversión de ejes cartesianos a isométrica:
isoX = cartX - cartY;
isoY = (cartX + cartY) / 2;
donde isoX e isoY son las coordenadas en perspectiva isométrica, mientras que cartX y cartY especifican las posiciones cartesianas en el plano 2D.

De forma similar se puede aplicar la transformación inversa:

// Conversión a ejes cartesianos
cartX = (2 * isoY + isoX) / 2;
cartY = (2 * isoY - isoX) / 2;

También es posible posicionar un objeto numerando la cuadrícula o grid de la pantalla con la posición (0,0) como eje de coordenadas, es decir, el centro de la pantalla estaría en (0,0).

GPL by Cburnett

De esta forma podemos posicionar un determinado objeto isométrico en el centro de su celda con la siguiente rutina:

screenX = (isoX - isoY) * mitadAncho;
screenY = (isoX + isoY) * mitadAlto; 
Aunque esta posición nos dará la esquina de la parte superior en coordenadas de pantalla. Para obtener las coordenadas centrales de la casilla será necesario sumar un offset:
 
screenX = ((isoX - isoY) * mitadAncho) + screenOrigenOffsetX;
screenY = ((isoX + isoY) * mitadAlto) + mitadAlto + screenOrigenOffsetY;
Finalmente para renderizar múltiples objetos en pantalla, es decir, todo el escenario, es preciso comenzar a dibujar desde atrás hacia delante (en orden de screenY) de la lista de objetos, pues de lo contrario podríamos tener algún efecto indeseable de overlapping.

Fuente:
(1) Véase Filmation.