El mapa
Estoy haciendo un juego de rol basado en mosaicos con Javascript, usando mapas de altura de ruido perlin, luego asigno un tipo de mosaico basado en la altura del ruido.
Los mapas terminan pareciéndose a esto (en la vista de minimapa).
Tengo un algoritmo bastante simple que extrae el valor de color de cada píxel en la imagen y lo convierte en un número entero (0-5) dependiendo de su posición entre (0-255) que corresponde a un mosaico en el diccionario de mosaicos. Esta matriz de 200x200 se pasa al cliente.
Luego, el motor determina los mosaicos a partir de los valores en la matriz y los dibuja en el lienzo. Entonces, termino con mundos interesantes que tienen características de aspecto realista: montañas, mares, etc.
Ahora, lo siguiente que quería hacer era aplicar algún tipo de algoritmo de mezcla que haría que los mosaicos se mezclaran perfectamente con sus vecinos, si el vecino no es del mismo tipo. El mapa de ejemplo anterior es lo que el jugador ve en su minimapa. En pantalla ven una versión renderizada de la sección marcada por el rectángulo blanco; donde los mosaicos se representan con sus imágenes en lugar de como píxeles de un solo color.
Este es un ejemplo de lo que el usuario vería en el mapa, ¡pero no es la misma ubicación que muestra la ventana de arriba!
Es desde este punto de vista que quiero que ocurra la transición.
El algoritmo
Se me ocurrió un algoritmo simple que atravesaría el mapa dentro de la ventana gráfica y representaría otra imagen sobre la parte superior de cada mosaico, siempre que estuviera al lado de un mosaico de diferente tipo. (¡No cambia el mapa! Solo renderizo algunas imágenes adicionales). La idea del algoritmo era perfilar a los vecinos del mosaico actual:
Este es un escenario de ejemplo de lo que el motor podría tener que representar, siendo el mosaico actual el marcado con la X.
Se crea una matriz 3x3 y se leen los valores a su alrededor. Entonces, para este ejemplo, la matriz se vería así.
[
[1,2,2]
[1,2,2]
[1,1,2]
];
Mi idea era resolver una serie de casos para las posibles configuraciones de mosaico. En un nivel muy simple:
if(profile[0][1] != profile[1][1]){
//draw a tile which is half sand and half transparent
//Over the current tile -> profile[1][1]
...
}
Lo que da este resultado:
Lo que funciona como una transición de [0][1]
a [1][1]
, pero no de [1][1]
a [2][1]
, donde queda un borde duro. Así que pensé que en ese caso tendría que usarse un mosaico de esquina. Creé dos hojas de sprites de 3x3 que pensé que contendrían todas las combinaciones posibles de fichas que podrían ser necesarias. Luego repliqué esto para todas las fichas que hay en el juego (Las áreas blancas son transparentes). Esto termina siendo 16 mosaicos para cada tipo de mosaico (no se utilizan los mosaicos centrales de cada hoja de sprites).
El resultado ideal
Entonces, con estos nuevos mosaicos y el algoritmo correcto, la sección de ejemplo se vería así:
Sin embargo, cada intento que hice ha fallado, siempre hay algún defecto en el algoritmo y los patrones terminan siendo extraños. Parece que no puedo resolver todos los casos y, en general, parece una mala forma de hacerlo.
¿Una solución?
Entonces, si alguien pudiera proporcionar una solución alternativa sobre cómo podría crear este efecto, o qué dirección tomar para escribir el algoritmo de creación de perfiles, ¡estaría muy agradecido!
fuente
Respuestas:
La idea básica de este algoritmo es utilizar un paso de preprocesamiento para encontrar todos los bordes y luego seleccionar el mosaico de suavizado correcto de acuerdo con la forma del borde.
El primer paso sería encontrar todos los bordes. En el ejemplo a continuación, las fichas de borde marcadas con una X son todas fichas verdes con una ficha marrón como una o más de sus ocho fichas adyacentes. Con diferentes tipos de terreno, esta condición podría traducirse en que un mosaico sea un mosaico de borde si tiene vecinos de un número de terreno más bajo.
Una vez que se detectan todos los mosaicos de bordes, lo siguiente que debe hacer es seleccionar el mosaico de suavizado correcto para cada mosaico de bordes. Aquí está mi representación de sus mosaicos de alisado.
Tenga en cuenta que en realidad no hay tantos tipos diferentes de mosaicos. Necesitamos los ocho mosaicos exteriores de uno de los cuadrados de 3x3, pero solo los cuatro cuadrados de las esquinas del otro, ya que los mosaicos de borde recto ya se encuentran en el primer cuadrado. Esto significa que en total hay 12 casos diferentes entre los que debemos distinguir.
Ahora, mirando un mosaico de borde, podemos determinar en qué dirección gira el límite mirando sus cuatro mosaicos vecinos más cercanos. Al marcar un mosaico de borde con X, tal como se muestra arriba, tenemos los siguientes seis casos diferentes.
Estos casos se utilizan para determinar el mosaico de suavizado correspondiente y podemos numerar los mosaicos de suavizado en consecuencia.
Todavía hay una opción de aob para cada caso. Esto depende de qué lado esté el césped. Una forma de determinar esto podría ser hacer un seguimiento de la orientación del límite, pero probablemente la forma más sencilla de hacerlo es elegir un mosaico al lado del borde y ver qué color tiene. La imagen a continuación muestra los dos casos 5a) y 5b) que se pueden distinguir, por ejemplo, verificando el color del mosaico superior derecho.
La enumeración final para el ejemplo original se vería así.
Y después de seleccionar el mosaico de borde correspondiente, el borde se vería así.
Como nota final, podría decir que esto funcionaría siempre que el límite sea algo regular. Más precisamente, los mosaicos de borde que no tienen exactamente dos mosaicos de borde, ya que sus vecinos deberán tratarse por separado. Esto ocurrirá para las fichas de borde en el borde del mapa que tendrá un solo borde vecino y para piezas de terreno muy estrechas donde el número de fichas de borde vecino podría ser tres o incluso cuatro.
fuente
El siguiente cuadrado representa una placa de metal. Hay una "ventilación de calor" en la esquina superior derecha. Podemos ver cómo a medida que la temperatura de este punto permanece constante, la placa de metal converge a una temperatura constante en cada punto, siendo naturalmente más caliente cerca de la parte superior:
El problema de encontrar la temperatura en cada punto se puede resolver como un "problema de valor límite". Sin embargo, la forma más sencilla de calcular el calor en cada punto es modelar la placa como una cuadrícula. Conocemos los puntos en la cuadrícula a temperatura constante. Configuramos la temperatura de todos los puntos desconocidos para que sean temperatura ambiente (como si la ventilación acabara de encenderse). Luego dejamos que el calor se extienda a través de la placa hasta llegar a la convergencia. Esto se hace por iteración: iteramos a través de cada punto (i, j). Establecemos punto (i, j) = (punto (i + 1, j) + punto (i-1, j) + punto (i, j + 1) + punto (i, j-1)) / 4 [a menos el punto (i, j) tiene una salida de calor de temperatura constante]
Si aplica esto a su problema, es muy similar, solo colores promedio en lugar de temperaturas. Probablemente necesite unas 5 iteraciones. Sugiero usar una cuadrícula de 400x400. Eso es 400x400x5 = menos de 1 millón de iteraciones que serán rápidas. Si solo usa 5 iteraciones, probablemente no tendrá que preocuparse por mantener los puntos de color constante, ya que no cambiarán demasiado de su original (de hecho, solo los puntos dentro de la distancia 5 del color pueden ser afectados por el color). Pseudocódigo:
fuente
Bien, los primeros pensamientos son que automatizar una solución perfecta al problema requiere algunas matemáticas de interpolación bastante carnosas. Basado en el hecho de que mencionas imágenes de mosaico pre-renderizadas, supongo que la solución de interpolación completa no está garantizada aquí.
Por otro lado, como dijiste, terminar el mapa a mano conducirá a un buen resultado ... pero también supongo que cualquier proceso manual para corregir fallas tampoco es una opción.
Aquí hay un algoritmo simple que no da un resultado perfecto, pero que es muy gratificante en función del bajo esfuerzo que requiere.
En lugar de intentar mezclar CADA mosaico de borde, (lo que significa que necesita saber primero el resultado de mezclar los mosaicos adyacentes - interpolación, o necesita refinar todo el mapa varias veces y no puede confiar en mosaicos pregenerados) ¿Por qué no mezclar fichas en un patrón de tablero de damas alternativo?
Es decir, ¿solo se mezclan los mosaicos destacados en la matriz de arriba?
Suponiendo que los únicos pasos permitidos en el valor son uno a la vez, solo tiene unos pocos mosaicos para diseñar ...
Habrá 16 patrones en total. Si aprovecha la simetría rotacional y reflexiva, habrá aún menos.
'A' sería un mosaico de estilo simple [1]. 'D' sería una diagonal.
Habrá pequeñas discontinuidades en las esquinas de los mosaicos, pero serán menores en comparación con el ejemplo que usted dio.
Si puedo, actualizaré esta publicación con imágenes más adelante.
fuente
Estaba jugando con algo similar a esto, no se terminó por varias razones; pero básicamente tomaría una matriz de 0 y 1, siendo 0 el suelo y 1 siendo una pared para una aplicación de generador de laberinto en Flash. Como AS3 es similar a JavaScript, no sería difícil volver a escribir en JS.
Básicamente, esto verifica cada mosaico a su alrededor, de izquierda a derecha, de arriba a abajo y supone que los mosaicos de borde son siempre 1. También me he tomado la libertad de exportar las imágenes como un archivo para usar como clave:
Esto está incompleto y probablemente sea una forma hacky de lograrlo, pero pensé que podría ser de algún beneficio.
Editar: captura de pantalla del resultado de ese código.
fuente
Sugeriría algunas cosas:
no importa cuál sea el mosaico "central", ¿verdad? podría ser 2, pero si todos los demás son 1, mostraría 1?
solo importa cuáles son las esquinas, cuando hay una diferencia en los vecinos inmediatos de la parte superior o lateral. Si todos los vecinos inmediatos son 1 y una esquina es 2, mostraría 1.
Probablemente precalcularía todas las combinaciones posibles de vecinos, creando una matriz de 8 índices con los primeros cuatro indicando los valores de los vecinos superiores / inferiores, y el segundo indicando las diagonales:
bordes [N] [E] [S] [W] [NE] [SE] [SW] [NW] = cualquier desplazamiento en el sprite
entonces en su caso, [2] [2] [1] [1] [2] [2] [1] [1] = 4 (el 5to sprite).
en este caso, [1] [1] [1] [1] sería 1, [2] [2] [2] [2] sería 2, y el resto tendría que ser resuelto. Pero la búsqueda de un mosaico en particular sería trivial.
fuente