Manual actualizado para crear mundos isométricos, parte 2

Lo que vas a crear

En esta parte final de la serie de tutoriales, desarrollaremos el primer tutorial y aprenderemos sobre la implementación de recolecciones, disparadores, cambio de nivel, búsqueda de rutas, seguimiento de ruta, desplazamiento de nivel, altura isométrica y proyectiles isométricos.

1. pastillas

Las recolecciones son elementos que se pueden recolectar dentro del nivel, normalmente al caminar sobre ellos, por ejemplo, monedas, gemas, efectivo, municiones, etc..

Los datos de recolección se pueden acomodar directamente en nuestros datos de nivel de la siguiente manera:

[[1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,8,0,0,1], [1,0,0, 8,0,1], [1,0,0,0,0,1], [1,1,1,1,1,1]]

En este nivel de datos, utilizamos 8 para denotar una recogida en un azulejo de hierba (1 y 0 representan paredes y azulejos transitables respectivamente, como antes). Esta podría ser una imagen de mosaico único con un mosaico de césped superpuesto con la imagen de recogida. Siguiendo esta lógica, necesitaremos dos estados de mosaico diferentes para cada mosaico que tenga un pickup, es decir, uno con pickup y otro sin mostrar después de que se recolecte el pickup..

El arte isométrico típico tendrá múltiples mosaicos transitables, supongamos que tenemos 30. El enfoque anterior significa que si tenemos N pastillas, necesitaremos N x 30 mosaicos además de los 30 mosaicos originales, ya que cada mosaico deberá tener una versión con pastillas y una sin ella. Esto no es muy eficiente; En su lugar, deberíamos tratar de crear dinámicamente estas combinaciones.. 

Para resolver esto, podríamos usar el mismo método que usamos para colocar al héroe en el primer tutorial. Cada vez que nos topemos con una baldosa de recolección, colocaremos una baldosa de hierba primero y luego colocaremos la recolección sobre la baldosa de hierba. De esta manera, solo necesitamos N mosaicos de recolección además de 30 mosaicos transitables, pero necesitaríamos valores numéricos para representar cada combinación en los datos de nivel. Para resolver la necesidad de valores de representación N x 30, podemos mantener una pickupArray para almacenar exclusivamente los datos de recogida, aparte de la levelData. El nivel completado con la recogida se muestra a continuación:

Para nuestro ejemplo, mantengo las cosas simples y no uso una matriz adicional para recolecciones.

Recogiendo recogidas

La detección de recolecciones se realiza de la misma manera que la detección de losetas de colisión, pero después moviendo el personaje.

if (onPickupTile ()) pickupItem ();  function onPickupTile () // verifica si hay una recuperación en el retorno de azulejo de héroe (levelData [heroMapTile.y] [heroMapTile.x] == 8);  

En la funcion onPickupTile (), comprobamos si el levelData valor de matriz en el heroMapTile coordenada es una baldosa de recogida o no. El numero en el levelData la matriz en esa coordenada de mosaico denota el tipo de recolección. Verificamos si hay colisiones antes de mover el personaje, pero luego necesitamos buscar recolecciones, porque en el caso de las colisiones, el personaje no debe ocupar el lugar si ya está ocupado por la ficha de colisión, pero en el caso de las recolecciones, el personaje puede moverse libremente. encima de eso.

Otra cosa a tener en cuenta es que los datos de colisión generalmente nunca cambian, pero los datos de recolección cambian cada vez que recogemos un artículo. (Esto generalmente implica cambiar el valor en el levelData matriz de, digamos, 8 a 0.)

Esto conduce a un problema: ¿qué sucede cuando necesitamos reiniciar el nivel y, por lo tanto, restablecer todas las pastillas de nuevo a sus posiciones originales? No tenemos la información para hacer esto, ya que el levelData Se ha cambiado la matriz cuando el jugador recogió objetos. La solución es usar una matriz duplicada para el nivel mientras está en juego y mantener el original levelData matriz intacta. Por ejemplo, usamos levelData y levelDataLive [], clone este último desde el principio al inicio del nivel, y solo cambie levelDataLive [] durante el juego.

Para el ejemplo, estoy generando una recolección aleatoria en un mosaico de césped vacío después de cada recolección e incrementando el pickupCount. los pickuptem la función se ve así.

function pickupItem () pickupCount ++; levelData [heroMapTile.y] [heroMapTile.x] = 0; // genera la siguiente recolección spawnNewPickup (); 

Debes notar que revisamos las recolecciones cuando el personaje está en esa casilla. Esto puede suceder varias veces en un segundo (verificamos solo cuando el usuario se mueve, pero podemos dar vueltas y vueltas dentro de una casilla), pero la lógica anterior no fallará; desde que configuramos el levelData datos de matriz a 0 La primera vez que detectamos un pickup, todos los posteriores. onPickupTile () los cheques regresaran falso por ese azulejo. Echa un vistazo al siguiente ejemplo interactivo:

2. Azulejos de disparo

Como su nombre lo sugiere, las fichas disparadas causan que algo suceda cuando el jugador las pisa o presiona una tecla cuando está en ellas. Podrían teletransportar al jugador a una ubicación diferente, abrir una puerta o engendrar un enemigo, para dar algunos ejemplos. En cierto sentido, las pastillas son solo una forma especial de fichas disparadoras: cuando el jugador pisa una ficha que contiene una moneda, la moneda desaparece y su contador de monedas aumenta..

Veamos cómo podemos implementar una puerta que lleve al jugador a un nivel diferente. La baldosa junto a la puerta será una baldosa de disparo; cuando el jugador presiona el X Clave, pasarán al siguiente nivel..

Para cambiar los niveles, todo lo que tenemos que hacer es cambiar la corriente levelData matriz con la del nuevo nivel, y establecer el nuevo heroMapTile Posición y dirección para el personaje héroe. Supongamos que hay dos niveles con puertas para permitir el paso entre ellos. Dado que la baldosa de tierra junto a la puerta será la baldosa de disparo en ambos niveles, podemos usar esta como la nueva posición para el personaje cuando aparezcan en el nivel.

La lógica de implementación aquí es la misma que para las recolecciones, y nuevamente usamos el levelData matriz para almacenar valores de disparo. Para nuestro ejemplo, 2 denota un azulejo de la puerta, y el valor al lado es el disparador. he utilizado 101 y 102 con la convención básica de que cualquier ficha con un valor mayor que 100 es una ficha de activación y el valor menos 100 puede ser el nivel al que lleva:

var level1Data = [[1,1,1,1,1,1], [1,1,0,0,0,1], [1,0,0,0,0,1], [2,102,0 , 0,0,1], [1,0,0,0,1,1], [1,1,1,1,1,1]]; var level2Data = [[1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,8,0,0,1], [1,0 , 0,0,101,2], [1,0,1,0,0,1], [1,1,1,1,1,1]];

El código para verificar un evento desencadenante se muestra a continuación:

var xKey = game.input.keyboard.addKey (Phaser.Keyboard.X); xKey.onUp.add (triggerListener); // agrega un detector de señal para la función de evento up triggerListener () var trigger = levelData [heroMapTile.y] [heroMapTile.x]; if (disparador> 100) // disparador de mosaico de disparador válido- = 100; if (trigger == 1) // cambia a nivel 1 levelData = level1Data;  else // cambia a nivel 2 levelData = level2Data;  para (var i = 0; i < levelData.length; i++)  for (var j = 0; j < levelData[0].length; j++)  trigger=levelData[i][j]; if(trigger>100) // encuentra la nueva casilla de disparo y coloca al héroe allí heroMapTile.y = j; heroMapTile.x = i; heroMapPos = nuevo Phaser.Point (heroMapTile.y * tileWidth, heroMapTile.x * tileWidth); heroMapPos.x + = (tileWidth / 2); heroMapPos.y + = (tileWidth / 2); 

La función triggerListener () verifica si el valor de la matriz de datos de disparo en la coordenada dada es mayor que 100. Si es así, encontramos a qué nivel debemos cambiar al restar 100 del valor de mosaico. La función encuentra la ficha disparadora en el nuevo. levelData, que será la posición de engendro de nuestro héroe. He hecho que el gatillo se active cuando X es publicado; Si solo escuchamos la tecla presionada, terminamos en un bucle en el que intercambiamos niveles siempre que la tecla se mantenga presionada, ya que el personaje siempre aparece en el nuevo nivel en la parte superior de una ficha disparadora..

Aquí hay una demostración de trabajo. Trata de recoger objetos caminando sobre ellos e intercambiando niveles de pie junto a las puertas y golpeando X.

3. proyectiles

UNA proyectil es algo que se mueve en una dirección particular con una velocidad particular, como una bala, un hechizo mágico, una bola, etc. Todo sobre el proyectil es igual que el personaje héroe, aparte de la altura: en lugar de rodar por el suelo, Los proyectiles a menudo flotan sobre ella a cierta altura. Una bala viajará por encima del nivel de la cintura del personaje, e incluso una bola puede necesitar rebotar.

Una cosa interesante a tener en cuenta es que la altura isométrica es la misma que la altura en una vista lateral 2D, aunque de menor valor. No hay conversiones complicadas involucradas. Si una bola está 10 píxeles por encima del suelo en coordenadas cartesianas, podría estar 10 o 6 píxeles por encima del suelo en coordenadas isométricas. (En nuestro caso, el eje relevante es el eje y).

Intentemos implementar una pelota que rebota en nuestro prado amurallado. Como un toque de realismo, añadiremos una sombra para el balón. Todo lo que tenemos que hacer es agregar el valor de altura de rebote al valor isométrico Y de nuestra bola. El valor de la altura de salto cambiará de un cuadro a otro dependiendo de la gravedad, y una vez que la pelota toque el suelo, cambiaremos la velocidad actual a lo largo del eje y.

Antes de abordar el rebote en un sistema isométrico, veremos cómo podemos implementarlo en un sistema cartesiano 2D. Representemos el poder de salto de la pelota con una variable. zValue. Imagina que, para empezar, la pelota tiene un poder de salto de 100, por lo que zValue = 100

Usaremos dos variables más: incrementValue, que comienza en 0, y gravedad, que tiene un valor de -1. En cada cuadro, restamos. incrementValue desde zValue, y restar gravedad desde incrementValue con el fin de crear un efecto de amortiguación. Cuando zValue alcanza 0, significa que la pelota ha llegado al suelo; En este punto, volteamos el signo de incrementValue multiplicándolo por -1, convirtiéndolo en un número positivo. Esto significa que la bola se moverá hacia arriba desde el siguiente cuadro, rebotando así..

Así es como se ve en el código:

if (game.input.keyboard.isDown (Phaser.Keyboard.X)) zValue = 100;  incrementValue- = gravedad; zValue- = incrementValue; if (zValue<=0) zValue=0; incrementValue*=-1; 

El código sigue siendo el mismo para la vista isométrica, con la ligera diferencia de que puede usar un valor más bajo para zValue para empezar. Vea a continuación cómo zValue Se añade a la isométrica. y Valor de la bola mientras se rinde..

function drawBallIso () var isoPt = new Phaser.Point (); // No es recomendable crear puntos en el bucle de actualización var ballCornerPt = new Phaser.Point (ballMapPos.x-ball2DVolume.x / 2, ballMapPos.y ball2DVolume .y / 2); isoPt = cartesianToIsometric (ballCornerPt); // encuentra la nueva posición isométrica para el héroe en la posición del mapa 2D gameScene.renderXY (ballShadowSprite, isoPt.x + borderOffset.x + shadowOffset.x, isoPt.y + borderOffset.y + shadowOffset.y false ); // dibujar la sombra para representar la textura gameScene.renderXY (ballSprite, isoPt.x + borderOffset.x + ballOffset.x, isoPt.y + borderOffset.y-ballOffset.y-zValue, false); // héroe dibujar textura

Echa un vistazo al siguiente ejemplo interactivo:

Comprenda que el papel desempeñado por la sombra es muy importante y se suma al realismo de esta ilusión. Además, tenga en cuenta que ahora estamos usando las dos coordenadas de pantalla (x e y) para representar tres dimensiones en coordenadas isométricas; el eje y en las coordenadas de pantalla también es el eje z en las coordenadas isométricas. Esto puede ser confuso!

4. Encontrar y seguir un camino

Pathfinding y ruta siguiente son procesos bastante complicados. Hay varios enfoques que utilizan diferentes algoritmos para encontrar la ruta entre dos puntos, pero como nuestro levelData es una matriz 2D, las cosas son más fáciles de lo que podrían ser. Tenemos nodos bien definidos y únicos que el jugador puede ocupar, y podemos verificar fácilmente si se pueden caminar.

Artículos Relacionados

  • A * Pathfinding para principiantes
  • Campo de vectores basado en objetivos Pathfinding
  • Acelerar A * Pathfinding con el algoritmo de búsqueda de punto de salto
  • El comportamiento de la dirección del "camino que sigue"

Una descripción detallada de los algoritmos de búsqueda de rutas está fuera del alcance de este artículo, pero intentaré explicar la forma más común en que funciona: el algoritmo de ruta más corta, del cual A * y los algoritmos de Dijkstra son implementaciones famosas..

Nuestro objetivo es encontrar nodos que conecten un nodo inicial y un nodo final. Desde el nodo inicial, visitamos los ocho nodos vecinos y los marcamos como visitados; este proceso central se repite para cada nodo recién visitado, recursivamente. 

Cada hilo rastrea los nodos visitados. Al saltar a los nodos vecinos, los nodos que ya han sido visitados se omiten (la recursión se detiene); de lo contrario, el proceso continúa hasta que alcanzamos el nodo final, donde finaliza la recursión y la ruta completa seguida se devuelve como una matriz de nodos. A veces, nunca se llega al nodo final, en cuyo caso el pathfinding falla. Por lo general, terminamos encontrando múltiples rutas entre los dos nodos, en cuyo caso tomamos la que tiene el menor número de nodos..

Pathfinding

No es prudente reinventar la rueda cuando se trata de algoritmos bien definidos, por lo que utilizaríamos las soluciones existentes para nuestros propósitos de búsqueda de ruta. Para usar Phaser, necesitamos una solución de JavaScript, y la que he elegido es EasyStarJS. Inicializamos el motor de búsqueda de caminos como abajo.

easystar = new EasyStar.js (); easystar.setGrid (levelData); easystar.setAcceptableTiles ([0]); easystar.enableDiagonals (); // queremos que la ruta tenga diagonales easystar.disableCornerCutting (); // no hay una ruta diagonal cuando se camina en las esquinas de la pared

Como el nuestro levelData Sólo tiene 0 y 1, Podemos pasarlo directamente como la matriz de nodos. Establecemos el valor de 0 como el nodo transitable. Habilitamos la capacidad de caminar en diagonal, pero deshabilitamos esto al caminar cerca de las esquinas de las baldosas no transitables. 

Esto se debe a que, si está habilitado, el héroe puede cortar la ficha no transitable mientras realiza una caminata diagonal. En tal caso, nuestra detección de colisión no permitirá que el héroe pase. Además, tenga en cuenta que en el ejemplo he eliminado por completo la detección de colisiones, ya que ya no es necesario para un ejemplo de paseo basado en AI. 

Detectaremos el toque en cualquier ficha libre dentro del nivel y calcularemos la ruta usando el findPath función. El método de devolución de llamada plotAndMove recibe la matriz de nodos de la ruta resultante. Marcamos el minimapa con el camino recién encontrado.

game.input.activePointer.leftButton.onUp.add (findPath) La función findPath () if (isFindingPath || isWalking) devuelve; var pos = game.input.activePointer.position; var isoPt = nuevo Phaser.Point (pos.x-borderOffset.x, pos.y-borderOffset.y); tapPos = isometricToCartesian (isoPt); tapPos.x- = tileWidth / 2; // ajuste para encontrar el mosaico correcto para el error debido al redondeo de tapPos.y + = tileWidth / 2; tapPos = getTileCoordinates (tapPos, tileWidth); if (tapPos.x> -1 && tapPos.y> -1 && tapPos.x<7&&tapPos.y<7)//tapped within grid if(levelData[tapPos.y][tapPos.x]!=1)//not wall tile isFindingPath=true; //let the algorithm do the magic easystar.findPath(heroMapTile.x, heroMapTile.y, tapPos.x, tapPos.y, plotAndMove); easystar.calculate();    function plotAndMove(newPath) destination=heroMapTile; path=newPath; isFindingPath=false; repaintMinimap(); if (path === null)  console.log("No Path was found."); else path.push(tapPos); path.reverse(); path.pop(); for (var i = 0; i < path.length; i++)  var tmpSpr=minimap.getByName("tile"+path[i].y+"_"+path[i].x); tmpSpr.tint=0x0000ff; //console.log("p "+path[i].x+":"+path[i].y);   

Camino que sigue

Una vez que tenemos la ruta como una matriz de nodos, debemos hacer que el carácter la siga.

Digamos que queremos hacer que el personaje camine hacia una casilla en la que hacemos clic. Primero debemos buscar una ruta entre el nodo que ocupa el personaje actualmente y el nodo donde hicimos clic. Si se encuentra una ruta exitosa, entonces necesitamos mover el carácter al primer nodo en la matriz de nodos configurándolo como el destino. Una vez que llegamos al nodo de destino, verificamos si hay más nodos en la matriz de nodos y, si es así, configuramos el siguiente nodo como destino, y así sucesivamente hasta que lleguemos al nodo final.

También cambiaremos la dirección del jugador según el nodo actual y el nuevo nodo de destino cada vez que alcancemos un nodo. Entre nodos, solo caminamos en la dirección requerida hasta que alcancemos el nodo de destino. Esta es una IA muy simple, y en el ejemplo se hace en el método aiWalk se muestra parcialmente abajo.

función aiWalk () if (path.length == 0) // path ha finalizado if (heroMapTile.x == destination.x && heroMapTile.y == destination.y) dX = 0; dY = 0; isWalking = false; regreso;  isWalking = true; if (heroMapTile.x == destination.x && heroMapTile.y == destination.y) // alcanzó el destino actual, establecer nuevo, cambiar de dirección // espere hasta que estemos unos pasos en el mosaico antes de girar los pasosTabla ++; sidestination.x) dX = -1;  else dX = 0;  si (heroMapTile.ydestination.y) dY = -1;  else dY = 0;  if (heroMapTile.x == destination.x) dX = 0;  else if (heroMapTile.y == destination.y) dY = 0;  //…

Nosotros hacer es necesario filtrar los puntos de clic válidos al determinar si hemos hecho clic dentro del área transitable, en lugar de un mosaico de pared u otro mosaico no transitable.

Otro punto interesante para codificar la IA: no queremos que el personaje se gire para enfrentar la siguiente casilla en la matriz de nodos tan pronto como haya llegado a la actual, ya que un giro tan inmediato da como resultado que nuestro personaje camine por los bordes de azulejos En su lugar, deberíamos esperar hasta que el personaje esté unos pocos pasos dentro de la casilla antes de buscar el siguiente destino. También es mejor colocar manualmente al héroe en el centro de la ficha actual justo antes de que giremos, para que todo se sienta perfecto..

Echa un vistazo a la demostración de trabajo a continuación:

5. Desplazamiento isométrico

Cuando el área de nivel es mucho más grande que el área de pantalla disponible, tendremos que hacerlo voluta.

El área visible de la pantalla se puede considerar como un rectángulo más pequeño dentro del rectángulo más grande del área de nivel completo. El desplazamiento es, esencialmente, simplemente mover el rectángulo interior dentro del más grande. Por lo general, cuando ocurre este desplazamiento, la posición del héroe sigue siendo la misma con respecto al rectángulo de la pantalla, comúnmente en el centro de la pantalla. Curiosamente, todo lo que necesitamos para implementar el desplazamiento es rastrear el punto de la esquina del rectángulo interior.

Este punto de esquina, que representamos en las coordenadas cartesianas, caerá dentro de un mosaico en los datos de nivel. Para el desplazamiento, incrementamos las posiciones x e y del punto de esquina en coordenadas cartesianas. Ahora podemos convertir este punto en coordenadas isométricas y usarlo para dibujar la pantalla.. 

Los valores recién convertidos, en el espacio isométrico, también deben ser la esquina de nuestra pantalla, lo que significa que son los nuevos (0, 0). Entonces, al analizar y dibujar los datos de nivel, restamos este valor de la posición isométrica de cada mosaico y podemos determinar si la nueva posición del mosaico cae dentro de la pantalla.. 

Alternativamente, podemos decidir que vamos a dibujar sólo un X x y Rejilla isométrica de azulejos en la pantalla para que el bucle de dibujo sea eficiente para niveles más grandes. 

Podemos expresar esto en pasos como tal:

  • Actualizar las coordenadas xyy del punto de la esquina cartesiana.
  • Convertir esto en espacio isométrico.
  • Resta este valor de la posición de dibujo isométrico de cada baldosa.
  • Dibuja solo un número predefinido limitado de fichas en la pantalla a partir de esta nueva esquina.
  • Opcional: dibuje el mosaico solo si la nueva posición del dibujo isométrico cae dentro de la pantalla.
var cornerMapPos = nuevo Phaser.Point (0,0); var cornerMapTile = nuevo Phaser.Point (0,0); var visibleTiles = nuevo Phaser.Point (6,6); // ... actualización de la función () // ... if (isWalkable ()) heroMapPos.x + = heroSpeed ​​* dX; heroMapPos.y + = heroSpeed ​​* dY; // mueve la esquina en dirección opuesta cornerMapPos.x - = heroSpeed ​​* dX; cornerMapPos.y - = heroSpeed ​​* dY; cornerMapTile = getTileCoordinates (cornerMapPos, tileWidth); // obtener el nuevo mapa de mapas de héroe heroMapTile = getTileCoordinates (heroMapPos, tileWidth); // depthsort & draw new scene renderScene ();  function renderScene () gameScene.clear (); // borra el cuadro anterior y luego dibuja nuevamente var tileType = 0; // Limitemos los bucles dentro del área visible var startTileX = Math.max (0,0-cornerMapTile.x); var startTileY = Math.max (0,0-cornerMapTile.y); var endTileX = Math.min (levelData [0] .length, startTileX + visibleTiles.x); var endTileY = Math.min (levelData.length, startTileY + visibleTiles.y); startTileX = Math.max (0, endTileX-visibleTiles.x); startTileY = Math.max (0, endTileY-visibleTiles.y); // comprueba la condición del borde para (var i = startTileY; i < endTileY; i++)  for (var j = startTileX; j < endTileX; j++)  tileType=levelData[i][j]; drawTileIso(tileType,i,j); if(i==heroMapTile.y&&j==heroMapTile.x) drawHeroIso();     function drawHeroIso() var isoPt= new Phaser.Point();//It is not advisable to create points in update loop var heroCornerPt=new Phaser.Point(heroMapPos.x-hero2DVolume.x/2+cornerMapPos.x,heroMapPos.y-hero2DVolume.y/2+cornerMapPos.y); isoPt=cartesianToIsometric(heroCornerPt);//find new isometric position for hero from 2D map position gameScene.renderXY(sorcererShadow,isoPt.x+borderOffset.x+shadowOffset.x, isoPt.y+borderOffset.y+shadowOffset.y, false);//draw shadow to render texture gameScene.renderXY(sorcerer,isoPt.x+borderOffset.x+heroWidth, isoPt.y+borderOffset.y-heroHeight, false);//draw hero to render texture  function drawTileIso(tileType,i,j)//place isometric level tiles var isoPt= new Phaser.Point();//It is not advisable to create point in update loop var cartPt=new Phaser.Point();//This is here for better code readability. cartPt.x=j*tileWidth+cornerMapPos.x; cartPt.y=i*tileWidth+cornerMapPos.y; isoPt=cartesianToIsometric(cartPt); //we could further optimise by not drawing if tile is outside screen. if(tileType==1) gameScene.renderXY(wallSprite, isoPt.x+borderOffset.x, isoPt.y+borderOffset.y-wallHeight, false); else gameScene.renderXY(floorSprite, isoPt.x+borderOffset.x, isoPt.y+borderOffset.y, false);  

Tenga en cuenta que el punto de esquina se incrementa en el opuesto dirección a la posición del héroe se actualiza mientras se mueve. Esto asegura que el héroe se quede donde está con respecto a la pantalla. Echa un vistazo a este ejemplo (usa las flechas para desplazarte, toca para aumentar la cuadrícula visible).

Un par de notas:

  • Mientras se desplaza, es posible que tengamos que dibujar mosaicos adicionales en los bordes de la pantalla, o bien podemos ver que los mosaicos desaparecen y aparecen en los extremos de la pantalla.
  • Si tiene mosaicos que ocupan más de un espacio, deberá dibujar más mosaicos en los bordes. Por ejemplo, si el mosaico más grande de todo el conjunto mide X por Y, entonces deberá dibujar X más mosaicos a la izquierda y a la derecha y Y más mosaicos a la parte superior e inferior. Esto asegura que las esquinas del mosaico más grande aún serán visibles cuando se desplace dentro o fuera de la pantalla.
  • Todavía tenemos que asegurarnos de no tener áreas en blanco en la pantalla mientras nos acercamos a los bordes del nivel.
  • El nivel solo debería desplazarse hasta que el mosaico más extremo se dibuje en el extremo de la pantalla correspondiente. Después de esto, el personaje debe continuar moviéndose en el espacio de la pantalla. sin El nivel de desplazamiento. Para esto, tendremos que rastrear las cuatro esquinas del rectángulo de la pantalla interior y, en consecuencia, regular la lógica de desplazamiento del jugador y el desplazamiento. ¿Estás preparado para el desafío de intentar implementarlo por ti mismo??

Conclusión

Esta serie está especialmente dirigida a los principiantes que intentan explorar mundos de juegos isométricos. Muchos de los conceptos explicados tienen enfoques alternativos que son un poco más complicados, y he elegido deliberadamente los más fáciles. 

Es posible que no cumplan con la mayoría de los escenarios que puede encontrar, pero el conocimiento adquirido se puede utilizar para desarrollar estos conceptos y crear soluciones más complicadas. Por ejemplo, la simple clasificación en profundidad implementada se interrumpirá cuando tengamos niveles de varios pisos y mosaicos de plataformas que se mueven de una historia a otra. 

Pero eso es un tutorial para otro momento..