Os sistemas L , pelo que sei *, são um conjunto de regras de substituição semelhantes a gramáticas que você pode aplicar recursivamente para obter resultados "orgânicos" interessantes.
As plantas são onde os L-Systems são freqüentemente usados, pois mostram muito crescimento recursivo (ou seja, o ramo se divide em mais ramos). Para um exemplo simples, mostrarei uma árvore "pirulito" gerada usando um 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)
Então, na geração 1, nós apenas começamos:
o
|
Na geração 2, seguimos cada uma das regras e substituímos as partes existentes de acordo com as regras. Substituímos as "bolas" por "dois bastões e bolas":
o o
\ /
|
Geração 3:
o o o o
\| |/
\ /
|
Em breve teremos uma linda árvore (de baixa qualidade)!
Para fazer isso no código, você pode fazer isso recursivamente (por exemplo, DFS), aplicando continuamente as regras nas mesmas partes até chegar a um fim arbitrário ou fazer isso de forma iterativa (por exemplo, BFS), como fizemos neste exemplo , executando uma regra "passar" em todos os elementos e repetindo várias etapas. Isso é:
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'
Muitos usos do L-Systems executam a etapa "crescer" usando a subdivisão - ou seja, as partes ficam cada vez menores à medida que são "crescidas", as partes maiores são divididas. Caso contrário, seu sistema crescente poderá começar a se sobrepor. Você verá no meu exemplo de árvore de pirulito, que eu garanti magicamente que os dois galhos não se sobrepõem ao meio, alterando a forma dos novos galhos. Vamos fazer o exemplo da cidade usando a subdivisão:
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)
Isso fará sentido em um minuto.
Geração 1:
+--------------------+
| |
| |
| |
| V |
| |
| |
| |
+--------------------+
Um único bloco vertical chato. (OV significa vertical.)
Geração 2: substituímos o bloco vertical por blocos horizontais por uma estrada vertical no meio
+--------------------+
| r |
| r |
| r |
| H r H |
| r |
| r |
| r |
+--------------------+
OR significa estrada! Dividi a divisão aleatoriamente, não queremos peças regulares chatas no PCG.
Geração 3: substituímos os blocos horizontais por blocos verticais separados por estradas horizontais. As estradas existentes ficam; não há regras para eles.
+--------------------+
| V r |
| r |
|rrrrrrrr |
| r V |
| V r |
| rrrrrrrrrrrrr|
| r V |
+--------------------+
Observe como as estradas se conectam, o que é bom. Repita isso várias vezes e você terminará com algo assim (descaradamente rasgado uma resposta relacionada ):
Observe que há muitos detalhes que não cobri e que esse resultado parece "obviamente" gerado - as cidades reais parecem um pouco diferentes. É isso que torna o PCG divertido / difícil. Existem inúmeras coisas que você pode fazer para ajustar e melhorar seus resultados, mas, como não está relacionado ao L-Systems, deixarei esta resposta aqui; espero que isso ajude você a começar.
* - Não estudei formalmente o L-Systems, embora tenha encontrado tipos específicos como gramáticas e vegetação de PCG; corrija-me se eu tiver alguma definição ou conceito errado