Utilizzo di L-Systems per generare proceduralmente città


10

Attualmente sto realizzando un'app che si concentra molto sui contenuti generati proceduralmente. Finora ho implementato con successo la generazione procedurale del terreno e della forma di una mappa usando il rumore simplex. Sono davvero contento di come appare. Ora sto cercando di fare lo stesso per le città. Devo solo generare un layout 2D delle strade e degli edifici. Ci ho pensato e sembra che molte persone suggeriscano di usare L-Systems. Tuttavia, non riesco a avvolgerli intorno. Ottengo il concetto, ma non l'implementazione nel codice. Qualcuno ha esempi di codice di L-Systems per città generate proceduralmente o suggerimenti su altri modi per gestire le città?



+1 Evviva i sistemi L !
Luser droog,

Un modo in cui ho fatto qualcosa di simile è stato quello di creare una griglia di nodi che rappresentano le intersezioni, quindi collegare casualmente nodi adiacenti. Crea un layout simile a un labirinto, ma le strade non sono tutte collegate, quindi la navigazione in realtà non è possibile.
user137

L'autorità comunemente citata per i sistemi a L generatori di città è il documento di Parish e Müller: Procedural Modeling of Cities . Ho anche trovato un eccellente esempio di implementazione . È un buon punto di partenza, ma a seconda del tuo esatto requisito, potresti dover cambiare alcune cose.
Anders Ryndel,

Risposte:


20

L-Systems , da quello che posso dire *, è un insieme di regole grammaticali di sostituzione che è possibile applicare ricorsivamente per ottenere risultati "organici" interessanti.

Le piante sono dove vengono spesso utilizzati i sistemi L, in quanto mostrano molta crescita ricorsiva (cioè il ramo si divide in più rami). Per un semplice esempio, mostrerò un albero "lecca-lecca" generato usando un sistema L:

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)

Quindi alla generazione 1, abbiamo solo l'inizio:

o
|

Alla generazione 2, seguiamo ciascuna delle regole e sostituiamo le parti esistenti secondo le regole. Sostituiamo le "palle" con "due bastoncini e palle":

o   o
 \ /
  |

Terza generazione:

o o   o o
 \|   |/
   \ /
    |

Presto avremo un bellissimo (schifoso) grande albero!

Per fare questo nel codice, puoi farlo in modo ricorsivo (cioè DFS), applicando continuamente le regole sulle stesse parti fino a raggiungere un fine arbitrario, oppure puoi farlo iterativamente (cioè BFS) come abbiamo fatto in questo esempio , eseguendo una regola "passa" su tutti gli elementi e ripetendo una serie di passaggi. Questo è:

ricorsivamente:

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')

iterativo:

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

Molti usi di L-Systems eseguono la fase di "crescita" usando la suddivisione - ovvero, le parti continuano a ridursi man mano che vengono "cresciute", le parti più grandi vengono semplicemente divise. Altrimenti il ​​tuo sistema in crescita potrebbe iniziare a sovrapporsi su se stesso. Vedrai nel mio esempio di albero di lecca-lecca, ho magicamente assicurato che i due rami non si sovrappongano nel mezzo cambiando la forma dei nuovi rami. Facciamo l' esempio della città usando la suddivisione:

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)

Questo avrà senso tra un minuto.

Prima generazione:

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

Un singolo blocco verticale noioso. (La V sta per verticale.)

Seconda generazione: sostituiamo il blocco verticale con blocchi orizzontali con una strada verticale nel mezzo

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

La r sta per strada! Ho distanziato casualmente la divisione, non vogliamo noiose parti regolari in PCG.

Generazione 3: sostituiamo i blocchi orizzontali con blocchi verticali divisi in parti con strade orizzontali. Le strade esistenti rimangono; non ci sono regole per loro.

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

Nota come le strade si collegano tra loro, il che è bello. Ripeti abbastanza volte e finirai con qualcosa del genere (strappato palesemente una risposta correlata ):

inserisci qui la descrizione dell'immagine

Nota che ci sono molti dettagli che non ho trattato e che questo risultato sembra generato "ovviamente" - le città reali sembrano in qualche modo diverse. Questo è ciò che rende il PCG divertente / difficile. Ci sono infinite cose che puoi fare per modificare e migliorare i tuoi risultati, ma essendo estranei a L-Systems, lascerò questa risposta qui; spero che questo ti aiuti a iniziare.

* - Non ho studiato L-Systems formalmente, anche se ho incontrato tipi specifici come grammatiche e vegetazione PCG; per favore correggimi se sbaglio definizioni o concetti


1

La risposta di @congusbongus è eccellente, vorrei aggiungere alcune cose.

I blocchi devono essere suddivisi in aree di costruzione in base a tutte le strade che li delimitano. Quando hai la strada tutt'intorno, il modello generale è un anello. Vedi questo link per esempio: http://oldurbanist.blogspot.fr/2012/01/city-blocks-spaces-in-between.html

(A seconda della densità, potrebbe non esserci spazio nel centro dell'anello, vedi kowloon).

Dopo aver eseguito i blocchi, è necessario generare gli edifici. Sono un po 'complicati e richiedono una generazione a due passaggi. Sono parzialmente interdipendenti: il tuo generatore non dovrebbe creare una finestra che si trova di fronte alla parete laterale dell'edificio successivo.

E per aggiungere vita a questo, potresti voler influenzare la generazione con ambienti come il terreno o una mappa economica: le strade (tranne a San Francisco) tendono ad aggirare grandi colline, invece di andare dritte e i tipi di case sono pesantemente influenzato dalla parte della città in cui si trovano.

divertiti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.