Estoy haciendo un juego con un mundo generado por procedimientos creado al comienzo del juego, que consta de varias áreas representadas por cuadrículas (por ejemplo, 8x8, 9x6, los tamaños serían idealmente arbitrarios). Se supone que estas áreas están conectadas entre sí a través de una lista de dependencias.
Existe una conexión cuando al menos 3 espacios de esa cuadrícula están expuestos entre esas dos áreas. En la celda central de esa área de conexión de 3 espacios está la puerta entre las áreas:
He estado tratando de encontrar una manera de conectarlos, pero se vuelve cada vez más complejo a medida que se necesitan más áreas al mismo tiempo.
Probé algunos prototipos de papel y, aunque es un proceso muy simple al hacerlo visualmente, no he encontrado un buen conjunto de expresiones matemáticas que me permita colocar habitaciones con la misma eficiencia por código.
Aquí hay un ejemplo "simple" con el que estoy luchando en este momento:
- El área 'a' necesita conectarse a 'b' y 'c'
- El área 'b' debe estar conectada a 'a' y 'd'
- El área 'c' debe conectarse a 'a' y 'd'
- El área 'd' necesita conectarse a 'b' y 'c'
Considere, por simplicidad, estamos colocando las habitaciones por su orden de aparición en la lista (he probado otras). Así que me estoy acercando a esto como su algoritmo de procedimiento estándar de Dungeon Generation.
Colocamos 'a' en cualquier lugar del tablero, ya que es la primera área. A continuación, elegimos un muro al azar y, dado que nada está conectado a ese muro, podemos colocar 'b' allí:
Ahora necesitamos colocar 'c', pero 'a' ya está en el tablero y tiene una pared ocupada, por lo que decidimos colocarlo en otra pared. Pero no todas las ubicaciones funcionarán, porque 'd' se acerca y también necesita conectarse a 'b' y 'c':
Intenté una posible limitación de que 2 habitaciones que tienen el mismo conjunto de dependencias no pueden estar en paredes opuestas, pero incluso eso no garantiza el éxito:
Y en otros casos, donde las áreas tienen diferentes tamaños, estar en paredes opuestas puede funcionar:
Además, no considerar una pared usada es una suposición defectuosa ya que descarta soluciones válidas:
He intentado buscar investigaciones sobre otros algoritmos de Generación de Procedimientos o similares, como los algoritmos Optimal Rectangle Packing y Graph Layout, pero generalmente esos algoritmos no tienen en cuenta todas las restricciones de este problema y son difíciles de mezclar.
Pensé en un montón de enfoques, incluida la colocación de un área y retroceso hasta encontrar una ubicación adecuada, pero parecen muy dependientes de la prueba y el error y son costosos en términos de cómputo. Pero, dada la extensa investigación sobre los dos últimos problemas que mencioné, ¿podría ser la única / mejor solución?
Solo quería ver si alguien ha tenido problemas similares en el pasado o si está dispuesto a ayudarme a resolver esto y darme algunos consejos sobre dónde debo comenzar con el algoritmo. O, en su defecto, tendré que buscar aflojar las restricciones que he establecido.
fuente
Respuestas:
Este es un problema genial. Creo que se puede resolver mediante la planificación de acciones en el espacio de las ubicaciones de las habitaciones.
Defina el estado del mundo de la siguiente manera:
Defina una restricción como:
Donde "adyacente" es como lo describió (compartiendo al menos 3 vecinos)
Se dice que una restricción se invalida cuando las dos habitaciones no son adyacentes y ambas habitaciones existen.
Defina un estado para que sea válido cuando:
Definir una acción como una ubicación de una habitación, dado un estado actual. La acción es válida siempre que el estado resultante de la acción sea válido. Por lo tanto, podemos generar una lista de acciones para cada estado:
Ahora, lo que queda es un gráfico , donde los Estados son nodos y las Acciones son enlaces. El objetivo es encontrar un Estado que es tanto válida, y todas las habitaciones han sido colocados. Podemos encontrar una ubicación válida buscando en el gráfico de manera arbitraria, quizás usando una búsqueda de profundidad primero. La búsqueda se verá más o menos así:
Ahora la calidad de la mazmorra generada dependerá del orden en que se consideren las habitaciones y las acciones. Puede obtener resultados interesantes y diferentes probablemente simplemente permutando aleatoriamente las acciones que realiza en cada etapa, haciendo así un recorrido aleatorio a través del gráfico de estado-acción. La eficacia de la búsqueda dependerá en gran medida de la rapidez con la que pueda rechazar estados no válidos. Puede ser útil generar estados válidos a partir de las restricciones siempre que desee encontrar acciones válidas.
fuente
Sus prioridades de generación están en conflicto. Al generar niveles, su primer objetivo debe ser una red de puntos planos (no superpuestos) conectados , independientemente de la escala. Luego proceda a crear habitaciones a partir de los puntos dentro de esa red. Crear formas de habitación primero es un error, generalmente hablando. Primero, cree la conectividad y luego vea qué formas de habitación se pueden acomodar dentro de eso.
Algoritmo General
Cree una cuadrícula de piso cuantizada de tamaño suficiente para soportar su nivel, usando una matriz o imagen 2D.
Esparce puntos al azar en este espacio vacío. Puede usar una verificación aleatoria simple en cada mosaico para ver si obtiene un punto, o usar la distribución estándar / gaussiana para dispersar puntos. Asigne un color / valor numérico único a cada punto. Estas son las identificaciones. (PD: si después de este paso sientes que necesitas ampliar tu espacio, hazlo).
Para cada punto generado de este tipo, en secuencia, haga crecer gradualmente un círculo de límites o un rectángulo de límites en un solo paso (generalmente una tasa de 0.5-1.0 celdas / píxeles por paso) en
x
yy
. Puede crecer todos los límites en paralelo, todos comenzando desde el tamaño cero en el mismo paso, o puede comenzar a cultivarlos en diferentes momentos y a diferentes velocidades, dando un sesgo al tamaño de los que comienzan antes (imagine que las plántulas crecen, donde algunas empezar tarde) Por "crecer" me refiero a rellenar los límites recién incrementados con el color / ID único para el punto de partida de esos límites. Una metáfora para esto sería sostener rotuladores contra el reverso de un trozo de papel y ver crecer las manchas de tinta de diferentes colores, hasta que se encuentren.En algún momento, los límites de algún punto y otro punto van a chocar, durante el paso de crecimiento. Este es el punto en el que debe dejar de aumentar los límites para esos dos puntos, al menos en el sentido uniforme descrito en el paso 3.
Una vez que haya aumentado los límites de todos los puntos lo más posible y haya detenido todos los procesos de crecimiento, tendrá un mapa que debe estar en gran medida, pero no completamente. Es posible que ahora desee empacar esos espacios vacíos, que supongo que son blancos, como si estuvieran coloreando una hoja de papel.
Post-proceso de relleno de espacio
Se puede usar una variedad de técnicas para completar los espacios vacíos / blancos que quedan, según el paso 5:
Perturbación
Como paso final para hacer que las cosas se vean más orgánicas, puede hacer perturbaciones de bordes en diversos grados, en las celdas de los bordes de las áreas. Solo asegúrese de no bloquear rutas de movimiento cruciales.
Teoría, por el interés
Esto es similar al enfoque adoptado en los Diagramas de Voronoi / Triangulación de Delaunay , excepto que en lo anterior no está creando bordes explícitamente; en cambio, cuando las áreas delimitadas chocan, el crecimiento cesa. Notarás que los diagramas de Voronoi llenan el espacio; Esto se debe a que no dejan de crecer simplemente al tocarlos, sino más bien por algún grado nominal de superposición. Podrías intentar algo similar.
fuente