Skip to content

Instantly share code, notes, and snippets.

@michidk
Last active August 22, 2024 04:03
Show Gist options
  • Save michidk/ced97c81388fa8a0d6d9 to your computer and use it in GitHub Desktop.
Save michidk/ced97c81388fa8a0d6d9 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
public class LSystemGenerator : MonoBehaviour
{
[Serializable]
class State
{
public float size;
public float angle;
public float x;
public float y;
public float dir;
public State Clone() {
return (State) this.MemberwiseClone();
}
}
[Serializable]
class Node
{
public int x, y;
public bool isStreet;
public Node(int x, int y)
{
this.x = x;
this.y = y;
}
}
public string input = "LSYG";
public float sizeValue = 15f;
public float sizeGrowth = -1.5f;
public float angleValue = 90f;
public float angleGrowth = 0f;
public Dictionary<char, string> rules = new Dictionary<char, string>();
public int width, height = 80;
public GameObject custom;
private Node[,] nodes;
private State state;
private Stack<State> states = new Stack<State>();
void Awake()
{
nodes = new Node[width, height];
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
nodes[x, y] = new Node(x, y);
rules.Add('L', "|-S!L!Y");
rules.Add('S', "[F[FF-YS]F)G]+");
rules.Add('Y', "--[F-)<F-FG]-");
rules.Add('G', "FGF[Y+>F]+Y");
}
void Start()
{
input = Replace(input);
Generate();
Draw();
}
public void Draw()
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (nodes[x, y].isStreet)
{
GameObject go = (GameObject)Instantiate(custom, new Vector3(x, y, 0), Quaternion.identity);
go.transform.parent = this.transform;
go.name = "Tile (" + x + "|" + y + ")";
}
}
}
}
public void Generate()
{
state = new State()
{
x = 40,
y = 40,
dir = 0,
size = sizeValue,
angle = angleValue
};
foreach (char c in input)
{
switch (c)
{
case 'F':
float newX = state.x + state.size * Mathf.Cos(state.dir * Mathf.PI / 180);
float newY = state.y + state.size * Mathf.Sin(state.dir * Mathf.PI / 180);
Debug.Log(state.x + " -" + state.y);
nodes[Mathf.RoundToInt(state.x), Mathf.RoundToInt(state.y)].isStreet = true;
nodes[Mathf.RoundToInt(newX), Mathf.RoundToInt(newY)].isStreet = true;
//TODO: draw line
state.x = newX;
state.y = newY;
break;
case '+':
state.dir += state.angle;
break;
case '-':
state.dir -= state.angle;
break;
case '>':
state.size *= (1 - sizeGrowth);
break;
case '<':
state.size *= (1 + sizeGrowth);
break;
case ')':
state.angle *= (1 + angleGrowth);
break;
case '(':
state.angle *= (1 - angleGrowth);
break;
case '[':
states.Push(state.Clone());
break;
case ']':
state = states.Pop();
break;
case '!':
state.angle *= -1;
break;
case '|':
state.dir += 180;
break;
}
}
}
public string Replace(string s)
{
StringBuilder sb = new StringBuilder();
foreach (char c in s)
{
if (rules.ContainsKey(c))
{
sb.Append(rules[c]);
}
else
{
sb.Append(c);
}
}
return sb.ToString();
}
}
@haim96
Copy link

haim96 commented Jan 3, 2018

your system is nice, it creates a structure of "buildings" where is "F". I can't figure how to implement the roads drawing...
I'm using a set of square tiles for the road. how can I figure which tile I need to place on each node and how to rotate it?

and by the way, this line create negetive number which cuase array out of index error:
float newY = state.y + state.size * Mathf.Sin(state.dir * Mathf.PI / 180);
if fixed it by multiply in -1 as quick fix...

as far as I understand 2 following lines do the same thing...
nodes[Mathf.RoundToInt(state.x), Mathf.RoundToInt(state.y)].isStreet = true;
nodes[Mathf.RoundToInt(newX), Mathf.RoundToInt(newY)].isStreet = true;

@tinchopunk
Copy link

Hi how can i use this example with your code?

2020-04-06_16-27-30

@monzeralkhalil
Copy link

Nice program! I'm looking to modifiy the tree growth algorithm in a way that I can assign a branch length and angle at each iteration. Does anyone have any idea how to do it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment