Este tutorial es una traducción y adaptación al español por Julio Laguna, Autorizada por el autor , del tutorial de TONYPA Tile Based Games sujeta a una licencia Creative Commons.
Creative Commons License

Juegos basados en tiles para Flash.
por Julio Laguna (Traducción) - Originalmente escrito en Inglés por TONYPA

1 Juegos basados en "tiles" para Flash.

1.28 ...mas rutas.

El algoritmo de busqueda Breadth-first que utilizamos en el capítulo anterior no es demasiado rápido. Por ese motivo vamos a buscar un algoritmo mas rápido, que nos permitirá utilizar mapas grandes sin "reventar" la ejecución de nuestro juego:




Best-First

Recordará del capítulo anterior, como la busqueda mediante Breadth-first se expandía por todos los nodos en todas direcciones. El sistema no tiene idea de donde está el objetivo buscado, simplemente se busca en todas partes. Por ello cuesta tanto encontrar la ruta. Por supuesto, siempre encuentra la ruta mas corta, pero seamos honestos, ¿que es mas importante, encontrar una ruta perfecta, o hacer que nuestro juego sea jugable?.

Así pues veamos una alternativa a la busqueda de rutas, el algoritmo Best-First. Ahora lo que haremos, será ver como de lejos está el nodo destino respecto al nodo origen, y anexaremos la distancia estimada con cada nodo.

cost = Math.abs(x-targetx)+Math.abs(y-targety);

Nos valemos de un valor de coste ("cost"), contamos los pasos a los que se encuentra el Tile destino del Tile origen en ambas direcciones x/y y sumamos los pasos en ambas direcciones.

La otra diferencia, es que mantenemos nuestro array Unchecked_Neighbours ordenado comenzando por el nodo con menor coste al objetivo (debe estar cerca del objetivo), hasta el nodo con mayor coste. Ordenando el array, siempre estaremos mirando en dirección al objetivo, antes de ir en otras direcciones. Sin embargo debo ponerle sobre aviso, aunque Best-First siempre encuentra una ruta, esta no siempre es la ruta mas corta. Sin embargo para muchos mapas este método es mas rápido y eficaz que el algoritmo A*.


Código para implementar Best-First

Vamos a partir del código utilizado en el capítulo 27. Modifique la función findPath. Necesitamos añadir coste al primer nodo:

var cost = Math.abs(startx-targetx)+Math.abs(starty-targety);
path[path.name]={x:startx, y:starty, parentx:null, parenty:null, cost:cost};

además hemos de pasar el nodo objetivo, como parametro a la función addNode:

addNode (N, N.x+1, N.y, targetx, targety);
addNode (N, N.x-1, N.y, targetx, targety);
addNode (N, N.x, N.y+1, targetx, targety);
addNode (N, N.x, N.y-1, targetx, targety);

Recuerde modificar la función addNode ya que ahora le pasamos el objetivo como parametro:

function addNode (ob, x, y, targetx, targety){
  path.name="node_"+y+"_"+x;
  if(game["t_"+y+"_"+x].walkable){
    if (path[path.name].cost==undefined) {
      var cost = Math.abs(x-targetx)+Math.abs(y-targety);
      path[path.name]={x:x, y:y, parentx:ob.x, parenty:ob.y, cost:cost};
      for(var i=0; i<path.Unchecked_Neighbours.length; i++){
        if (cost<path.Unchecked_Neighbours[i].cost){
          path.Unchecked_Neighbours.splice(i, 0, path[path.name]);
          break;
        }
      }
      if (i>=path.Unchecked_Neighbours.length) {
        path.Unchecked_Neighbours[path.Unchecked_Neighbours.length]=path[path.name];
      }
    }
  }
}

Comprobamos si el nodo ya está creado, si se trata de un nuevo nodo, se le calcula el coste.

Tras hacer un nuevo nodo, comenzamos un bucle que recorre el array Unchecked_Neighbours y que compara el coste del nodo actual con cada elemento del array. Finalizamos el bucle si encontramos un nodo en el array que tiene un coste mayor (ya que está ordenado). Insertamos el nodo en esa posición del array, de modo que este se mantienen ordenado por coste.

La última instrucción if comprueba si hemos recorrido todo el array sin encontrar ningún elemento con un coste mayor, en cuyo caso el mayor coste es del nodo actual, y por ello se insertará al final del array.


Mas rápido, mas rápido !

Si la busqueda de rutas tarda demasiado, debido a que tienen mapas muy grandes, debiera considerar precalcular algunos tramos del mapa utilizando marcas o checkpoints para llevar al personaje de un sitio a otro del mapa sin mirar por todos los Tiles.

Michael Grundvig ha creado un sistema de busqueda de rutas extremadamente rápido utilizando rutas precalculadas. Puede leer sobre esto aquí. Él calcula todas las rutas de cada Tile al resto de Tiles y almacena estas rutas con los mapas. Cuando un personaje quiere ir de un lugar del mapa a otro, simplemente escoje la ruta adecuada y realiza el movimiento.

Los marcadores o checkpoints trocearán virtualmente el mapa en mapas mas pequeños, conectados por rutas predefinidas. Así lo único que necesita, es encontrar a que minimapa pertenecen los puntos de origen y destino de la ruta, y utilizar la ruta memorizada para desplazarse de un punto a otro. Esto permite utilizar rutas en mapas extremadamente grandes.

Otra forma de calculo de rutas grandes sin ralentizar el juego, es desplegar el calculo de rutas sobre varios fotogramas. Para ello será necesario "romper" el bucle de calculo de rutas tras un cierto número de pasos, recordar el estado actual, y continuar con la ejecución de cualquier otro código del juego necesario para en el siguiente paso continuar buscando la ruta. Andre Michelle ha posteado un ejemplo bastante bueno de esta idea aquí.

Como habrá observado a lo largo del capítulo anterior y de este mismo, el calculo de rutas es un área que comprende muchas posibles soluciones. Es tarea nuestra buscar las que mejor se adapten para lograr un equilibrio entre resultados y jugabilidad de nuestros juegos. Puede encontrar muchas estrategias en libros e internet que le facilitarán la consecución de sus objetivos.

En el siguiente capítulo abordaremos un tema también peliagudo de los juegos de Tiles. Las pendientes o cuestas.

« Anterior
Indice
Siguiente »