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