Mapas isométricos en Flash

Tiles dibujables

Debido a que el mapa puede ser mucho más grande que la pantalla, no es factible dibujar todos los tiles del mapa en cada frame. Por lo tanto, antes de dibujar el mapa se debe verificar que tiles entran dentro del puerto de visualización para luego iterar por las filas y las columnas que corresponden.

Figura 8. El viewport muestra sólo parte del mapa isométrico

Figura 8. El viewport muestra sólo parte del mapa isométrico

Tenemos la posición de viewport y su tamaño. Deseamos conocer en cada vértice extremo del vierport a que fila y columna del mapa corresponde. Por lo tanto, deseamos convertir de coordenadas de mundo plano a coordenadas de mapa.


rowcol = GetRowColFromPlaneWorldCoords(Viewport._x + Viewport._width, Viewport._y + Viewport._height);
var rowTo = Util.ClampTo(rowcol[0]+1, 0, ROWS);


rowcol = GetRowColFromPlaneWorldCoords(Viewport._x, Viewport._y);
var rowFrom = Util.ClampTo(rowcol[0], 0, ROWS);

rowcol = GetRowColFromPlaneWorldCoords(Viewport._x + Viewport._width, Viewport._y);
var colTo = Util.ClampTo(rowcol[1]+1, 0, COLS);

rowcol = GetRowColFromPlaneWorldCoords(Viewport._x, Viewport._y + Viewport._height);
var colFrom = Util.ClampTo(rowcol[1], 0, COLS);

Luego, podremos iterar por las filas y las columnas que ingresen en el rango especificado.

Dibujado del mapa en Flash

Utilizando la plataforma de desarrollo Macromedia Flash MX 2004, cada bloque del mapa será un movieclip que se creará dinámicamente y se agregará a un movieclip del referencia donde estará contenida toda la escena.

Ya hemos mencionado que no es factible dibujar todo el mapa cuando sólo se está viendo parte de él. Pues tampoco podremos crear tantos movieclips como bloques tengan nuestro mapa porque esto sería un derroche de recursos.

Crearemos tantos movieclips como bloques entren dentro del viewport en el peor caso, luego nos manejaremos con estos movieclips y en el caso de estar visualizando menos de la cantidad total creada, ocultaremos el exedente.

Nunca crearemos ni destruiremos movieclips en tiempo de dibujado.

Veamos ahora el algoritmo para la creación de los movieclips correspondientes cuando el mapa es construido:


// Calcula la cantidad máxima de tiles que entran en el puerto de visualización
var maxCols = Math.ceil(Viewport._width / (tw / 2));
var maxRows = Math.ceil(Viewport._height / (th / 2));

// Creo la cantidad de tiles máxima
for (var i=0; i<maxCols * maxRows; i++)
{
_mcBlocks[i] = targetClip.attachMovie(mcBlocks, "bck_" + i, i);
_mcBlocks[i]._visible = false;
}

// Guardo la cantidad máxima de movieclips que habrá en escena
_maxMovieClipCount = maxCols * maxRows;

Donde:

_mcBlocks: Es un array de movieclips.
tw: Ancho de cada tile.
th: Alto de cada tile.
col: Columna actual procesada.
row: Fila actual procesada.

Profundidad

Otro de los aspectos a tener en cuenta para la correcta visualización de los objetos que contendrá el mapa será el orden correcto en el dibujado de los mismos (si es que el dispositivo no posee algún mecanismo que haga de z-buffer).

Figura 9. El objeto delante y detrás de la referencia

Figura 9. El objeto delante y detrás de la referencia

En la figura anterior se pueden apreciar los dos casos clásicos. A la izquierda el objeto verde se encuentra sobre el rojo, pero a derecha esto es al revés.

En Macromedia Flash cada movie clip posee una profundidad (no es posible que dos movieclips se encuentren en la misma profundidad). La clase MovieClip posee un método llamado swapDepths que permite alterar la profundida de un movie clip por la de otro.

Entonces el objeto verde debe poder advertir esta situación y alernar su profundidad por la del objeto rojo ¿Y cómo hacemos esto? Como primer medida deberemos asignar a cada celda una profundidad calculada. La idea será que los valores mantengan la misma relación que en la siguiente figura:

figure10

Figura 10. Ejemplo de valores de profundidad

Aunque no es necesario que todos los valores de profundidad sean adyacentes.

La cuenta que podremos utilizar para calcular la profundidad es la siguiente:

prof. del bloque = ((COLS - col) * tw + row * th * COLS) * 2;

donde

COLS: Cantidad total de columnas.
tw: Ancho de cada tile.
th: Alto de cada tile.
col: Columna actual procesada.
row: Fila actual procesada.

Todos los bloques poseerán profundidades pares, Luego los objetos móviles poseerán profundidades impares lo cual asegurará que nunca objeto suplante un movieclip de tile existente.

Notar que el cálculo no arrojará los números expresados por la última figura sino que arrojará números que mantegan la misma relación relativa entre las celdas. Los valores reales según la fórmula propuesta será:

Suponiendo tw = 30 y th = 15:

Figura 11. Las profundidades calculadas para nuestro ejemplo

Figura 11. Las profundidades calculadas para nuestro ejemplo

Ahora bastará obtener el tile sobre el cual está parado el personaje cada momento e intercambiar el depth con el valor de la profundidad de la celda en cuestión más uno:

_spriteMovieClip.swapDepths(tileblockdepth + 1);

Conversión de coordenadas

Coordenadas de mundo isométricas a coordenadas de mundo plano

Las primitivas fundamentales del motor de mapas isométricos es la correcta y eficiente conversión de coordenadas. Veamos como pasar de una a la otra.

Tal vez la conversión más importante sea la de mundo isométrico a mundo plano. Aquí pasaremos de un sistema de tres coordenadas a un sistema de dos coordenadas y lo que haremos será básicamente una proyección ortogonal de una posición.

Figura 12. Los dos sistemas de coordenadas

Figura 12. Los dos sistemas de coordenadas

Para realizar la conversión emplearemos dos ecuaciones sencillas:

Figura 13. Cálculo a partir de proyecciones

Figura 13. Cálculo a partir de proyecciones

Proyectaremos Xi e Zi sobre X, siendo la suma de estas proyecciones Xp. Luego proyectaremos Xi e Zi sobre Y, siendo su diferencia Yp:

Xp = Xi * Cos ?1 + Zi * Cos ?2
Yp = Xi * Sen ?1 – Zi * Sen ?2

Pero en todo mapa isométrico, el ángulo ?1 = ?2 (de otro modo no sería isométrico), entonces:

Xp = Xi * Cos ? + Zi * Cos ?
Yp = Xi * Sen ? – Zi * Sen ?

y:

Xp = (Xi + Zi) Cos ?
Yp = (Xi – Zi) Sen ?

Pero el cálculo de Yp no es del todo correcto, no estamos teniendo en cuenta la coordenada Yi que no modifica a Xp pero si modifica a Yp. Su agregado a nuestra segunda fórmula es muy simple:

Yp = (Xi – Zi) Sen ? + Yi

Por último, nuestro mapa no siempre posee su origen en el (0, 0) del mundo. Podría estar ubicado en cualquier otra posición por lo tanto agregaremos la posibilidad de que esto pueda ser así:

Xp = Xmapa + (Xi + Zi) Cos ?
Yp = Ymapa – [(Xi - Zi) Sen ? + Yi]

Tengamos en cuenta que ? siempre es:

alfa = ArcTg(th / tw)

Notar que todas las operaciones trigonométricas pueden ser precalculadas cuando el mapa es construido ya que la relación entre alto y ancho del tile se mantiene constante.

Compartir:
  • Twitter
  • Facebook
  • MySpace
  • BarraPunto
  • del.icio.us

Pages: 1 2 3 4

Comments

  1. Ari Romero
    April 4th, 2010 | 18:11

    MUY interesante esto =) felicitaciones por el trabajo

Leave a reply