Uso de L-Systems para generar ciudades procesales

10

Actualmente estoy creando una aplicación que se centra mucho en el contenido generado por procedimientos. Hasta ahora, he implementado con éxito la generación de procedimientos del terreno y la forma de un mapa utilizando ruido simplex. Estoy realmente satisfecho con cómo se ve. Ahora estoy tratando de hacer lo mismo para las ciudades. Solo necesito generar un diseño en 2D de las calles y edificios. Lo he investigado y parece que la mayoría de la gente sugiere usar L-Systems. Sin embargo, parece que no puedo entenderlos. Entiendo el concepto, pero no la implementación en el código. ¿Alguien tiene algún ejemplo de código de L-Systems para ciudades generadas por procedimientos, o sugerencias sobre otras formas de manejar las ciudades?

pasawaya
fuente
¡Hurra +1 para L-Systems !
luser droog
Una forma en que hice algo similar fue crear una cuadrícula de nodos que representaban las intersecciones, y luego conectar aleatoriamente nodos adyacentes. Es un diseño similar a un laberinto, pero las calles no están todas conectadas, por lo que en realidad no es posible navegar.
usuario137
La autoridad comúnmente citada para los sistemas L que generan ciudades es el artículo de Parish y Müller: Modelado de procedimientos de ciudades . También encontré un excelente ejemplo de implementación . Es un buen lugar para comenzar, pero dependiendo de sus requisitos exactos, es posible que tenga que cambiar algunas cosas.
Anders Ryndel

Respuestas:

20

Los sistemas L , por lo que puedo decir *, son un conjunto de reglas de sustitución gramaticales que puede aplicar de forma recursiva para obtener resultados interesantes, "orgánicos".

Las plantas son donde a menudo se usan los sistemas L, ya que muestran un gran crecimiento recursivo (es decir, las ramas se dividen en más ramas). Para un ejemplo simple, mostraré un árbol "lollipop" generado usando un L-System:

variables : | o              (these are the things that will grow)
start  : o
         |                   (this is what we start with)
rules  : (o  o   o)         (these are the substitution rules that we apply
               \ /            one step at a time)

Entonces, en la generación 1, solo tenemos el comienzo:

o
|

En la generación 2, seguimos cada una de las reglas y sustituimos las partes existentes de acuerdo con las reglas. Reemplazamos las "bolas" con "dos palos y bolas":

o   o
 \ /
  |

Generación 3:

o o   o o
 \|   |/
   \ /
    |

¡Pronto tendremos un árbol grande y bonito!

Para hacer esto en código, puede hacer esto de forma recursiva (es decir, DFS), aplicando continuamente las reglas en las mismas partes hasta llegar a un final arbitrario, o puede hacerlo de forma iterativa (es decir, BFS) como lo hemos hecho en este ejemplo , realizando una regla "pasar" en todos los elementos y repitiendo una serie de pasos. Es decir:

Recursivamente:

tree = start
grow(tree, start)

func grow(tree, part)
    if this part of the tree is big enough
        stop
    if part is 'o'
        replace part with 'o\/o'
        grow(tree, the left 'o')
        grow(tree, the right 'o')

Iterativamente:

tree = start
for a number of iterations
    for each part in tree
        if part is 'o':
            replace with 'o\/o'

Muchos de los usos de L-Systems realizan el paso de "crecimiento" utilizando la subdivisión, es decir, las partes se vuelven más pequeñas a medida que "crecen", las partes más grandes simplemente se dividen. De lo contrario, su sistema de crecimiento puede comenzar a superponerse sobre sí mismo. Verás en mi ejemplo del árbol de piruletas, mágicamente me aseguré de que las dos ramas no se superpongan en el medio al cambiar la forma de las nuevas ramas. Hagamos el ejemplo de la ciudad usando la subdivisión:

variables: block_vertical block_horizontal road_vertical road_horizontal
start: block_vertical
rules: (block_vertical  block_horizontal road_vertical block_horizontal)
       (block_horizontal  block_vertical road_horizontal block_vertical)

Esto tendrá sentido en un minuto.

Generación 1:

+--------------------+
|                    |
|                    |
|                    |
|        V           |
|                    |
|                    |
|                    |
+--------------------+

Un solo bloque vertical aburrido. (La V significa vertical).

Generación 2: reemplazamos el bloque vertical con bloques horizontales con una carretera vertical en el medio

+--------------------+
|       r            |
|       r            |
|       r            |
|   H   r      H     |
|       r            |
|       r            |
|       r            |
+--------------------+

¡La r significa camino! He separado aleatoriamente la división, no queremos partes regulares aburridas en PCG.

Generación 3: reemplazamos los bloques horizontales con bloques verticales divididos con caminos horizontales. Los caminos existentes permanecen; No hay reglas para ellos.

+--------------------+
|   V   r            |
|       r            |
|rrrrrrrr            |
|       r      V     |
|   V   r            |
|       rrrrrrrrrrrrr|
|       r      V     |
+--------------------+

Observe cómo las carreteras se conectan entre sí, lo cual es bueno. Repita esto suficientes veces y terminará con algo como esto (descaradamente arrancó una respuesta relacionada ):

ingrese la descripción de la imagen aquí

Tenga en cuenta que hay muchos detalles que no he cubierto, y que este resultado se ve "obviamente" generado: las ciudades reales se ven algo diferentes. Eso es lo que hace que PCG sea divertido / difícil. Hay infinitas cosas que puede hacer para ajustar y mejorar sus resultados, pero al no estar relacionado con L-Systems, dejaré esta respuesta aquí; Espero que esto te ayude a comenzar.

* - No he estudiado L-Systems formalmente, aunque he encontrado tipos específicos como gramáticas y vegetación PCG; corríjame si me equivoco con alguna definición o concepto

congusbongus
fuente
1

La respuesta de @congusbongus es excelente, permítanme agregar algunas cosas.

Los bloques deben dividirse en áreas de construcción de acuerdo con todos los caminos que los bordean. Cuando tienes una carretera alrededor, el patrón general es un anillo. Consulte este enlace, por ejemplo: http://oldurbanist.blogspot.fr/2012/01/city-blocks-spaces-in-between.html

(Dependiendo de la densidad, puede que no haya espacio en el centro del anillo, ver kowloon).

Una vez que hayas hecho los bloques, debes generar los edificios. Son un poco difíciles y requieren una generación de dos pases. Son parcialmente interdependientes: su generador no debe crear una ventana que esté frente a la siguiente pared lateral del edificio.

Y para darle vida a esto, es posible que desee influir en la generación con entornos como el terreno o un mapa económico: las carreteras (excepto en San Francisco) tienden a rodear grandes colinas, en lugar de ir en línea recta y los tipos de casas son muy pesados. influenciado por la parte de la ciudad en la que se encuentran.

que te diviertas.

Lionel Barret
fuente