It looks like you're new here. If you want to get involved, click one of these buttons!
Hey all,
Today I was reading about the upcoming game No Man's Sky and its procedural generation techniques, and I basically read up on every one. I was intruiged by L-Systems, so I decided to first make my own "turtle" graphics drawing thing(which basically generates one continuous line as in the Logo programming language I believe). Then, I coded my own l-system interpreter. If you don't know what an L-System is, here is the wikipedia page: https://en.m.wikipedia.org/wiki/L-system
The code is quite neat for me, but still a bit hard to understand but its also really hard to explain. The wikipedia page should explain some of it, but not my thought process but here's the code anyway(I have it pre-loaded with the Sierpinski's Triangle Fractal but it can generate any of the ones on the wikipedia page and more with a bit of fiddling):
--# lSystem
lSystem = class()
function lSystem:init(var,const,axiom,rules,n,turtle,funcs)
-- you can accept and set parameters here
self.vars = var
self.consts = const
self.cur = axiom
self.finds = {}
self.replaces = {}
self.funcs = funcs
for i,ii in string.gmatch(rules, "(["..self.vars.."]+)>(["..self.vars..self.consts.."]+)") do
print(i,ii)
table.insert(self.finds,i)
table.insert(self.replaces,ii)
end
local count = 0
while count < n do
local newString = ""
for i=1,string.len(self.cur),1 do
local replaced = false
for a,b in pairs(self.finds) do
if string.sub(self.cur,i,i) == b and replaced == false then
newString = newString .. self.replaces[a]
replaced = true
break
else
end
end
if replaced == false then
newString = newString .. string.sub(self.cur,i,i)
end
end
self.cur = newString
count = count + 1
end
print(self.cur)
self:interpret(self.cur)
end
function lSystem:interpret(s)
-- Codea does not automatically call this method
for i=1,string.len(s),1 do
for a,v in pairs(self.funcs) do
if string.sub(s,i,i) == v.char then
v.func()
end
end
end
end
function lSystem:touched(touch)
-- Codea does not automatically call this method
end
--# Main
-- Turtle Graphics
-- Use this function to perform your initial setup
function setup()
print("Hello World!")
t = Turtle(0,0)
t:rotate(0)
local funcs = {
{char = "a",func = function() t:moveForward(10) end},
{char = "b",func = function() t:moveForward(10) end},
{char = "+",func = function() t:rotate(60) end},
{char = "-",func = function() t:rotate(-60) end},
}
l = lSystem("ab","+-","a","a>+b-a-b+;b>-a+b+a-",8,t,funcs)
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(255, 255, 255, 255)
-- Do your drawing here
scale(1/3)
t:draw()
end
function replaceString(s,i,p)
return string.sub(s,1,i-1) .. p .. string.sub(s,i+1)
end
--# Turtle
Turtle = class()
function Turtle:init(x,y)
-- you can accept and set parameters here
self.pos = vec2(x,y)
self.stack = {}
self.rot = 0
self.lines = {}
end
function Turtle:draw()
-- Codea does not automatically call this method
stroke(0)
for i,v in pairs(self.lines) do
strokeWidth(5)
line(v.p1.x,v.p1.y,v.p2.x,v.p2.y)
end
end
function Turtle:move(vec)
local opos = self.pos
local newpos = self.pos + vec
self.pos = self.pos + vec
self:addLine(opos,newpos)
end
function Turtle:moveForward(amount)
local opos = self.pos
local newpos = self.pos + vec2(amount,0):rotate(math.rad(self.rot))
self.pos = self.pos + vec2(amount,0):rotate(math.rad(self.rot))
self:addLine(opos,newpos)
end
function Turtle:rotate(amount)
self.rot = self.rot + amount
end
function Turtle:push()
table.insert(self.stack, {pos = vec2(self.pos.x,self.pos.y),rot = self.rot})
end
function Turtle:pop()
self.pos = self.stack[#self.stack].pos
self.rot = self.stack[#self.stack].rot
table.remove(self.stack,#self.stack)
end
function Turtle:addLine(pos1,pos2)
self.lines[#self.lines+1] = {p1 = pos1,p2 = pos2}
end
Comments
@TheSolderKing Nice program. I haven't looked thru the code yet, but I was wondering what it would take to add a parameter to vary the draw speed. It might be interesting to watch it drawing the triangle.
@TheSolderKing I didn't know what an L-System was, so I went to the link you showed. I found it very interesting. I was still determined to show the Sierpinski Triangle as it was created, but I didn't want to try and figure out your code. I wrote my own version for the triangle. I was able to show it as it was being created, but I didn't like the way it was do it. The drawing was jumping around because of the translates, so I removed that part of the code. But anyways, here's my code which draws the triangle when complete. I'm still determined to have it draw the triangle as its being created.
Here's a version that shows the Sierpinski Triangle being created. You can display the parameter window to change the speed as it's running.
That's pretty interesting, thanks for sharing all!
@TheSolderKing
Could you link to the article about the no mans sky techniques?
https://en.m.wikipedia.org/wiki/No_Man's_Sky
lol just the wikipedia page.
But also:
http://venturebeat.com/2015/06/21/how-no-mans-sky-creator-is-using-clever-tech-to-build-a-truly-indie-game-universe/
Ah I should've checked wikipedia myself, many thanks!
I wrote a couple of procedural plant/tree/bush functions that I'd like to improve with this. Very cool technique!