-
-
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(); | |
} | |
} |
Hey,
you have to find a good L-System first. I recommend playing around with this tool: http://www.kevs3d.co.uk/dev/lsystems/. Then just use Draw() to draw your road network using your GameObjects.
Read more about L-Systems over here: http://algorithmicbotany.org/papers/abop/abop-ch1.pdf
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;
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?
HI,
how can i use this to create a procedural city (with roads and buildings)? thank!