Hello guys i want to give you a guide to making fractal trees, this tutorial is for an above average scripter.
Also I may have just learned OOP…
What is a fractal tree?
- A fractal tree can be loosely defined as a trunk and a number of branches that each look like the tree itself, thus creating a self-similar object.
How do we make it in roblox?
- To make a fractal tree you first have to have a good understanding of basic math and how to move parts around aka CFraming.
Steps
- First we need to be able to to stack parts of different sizes
How do we do this? You have to know/remember that positioning an object utilizes the center of it.
So we need to place the center of a new part at the top of surface of the current part
--OldPart will be the current part
local Size = 3 -- we will end up using this for branch size
local Part = Instance.new("Part")
Part.Anchored = true
Part.Parent = game.Workspace
Part.Size = Vector3.new(Size,OldPart.Size.Y * .5,Size) -- The height of the new part is 50% or 1/2 of the old part
Part.CFrame = OldPart.CFrame * CFrame.new(0,OldPart.Size.Y/2,0)
So now we need to move it up the length of the new part
to do this you would need to do…
Part.CFrame = OldPart.CFrame * CFrame.new(0,OldPart.Size.Y/2 + Part.Size.Y/2,0)
So now that we know how to stack parts we have to stick it in a repeating function otherwise known as a recursive function, a recursive function is a function that calls itself.
--For this demonstration ill be using oop
local middleclass = {}
middleclass.__index = middleclass
local rotate = CFrame.fromEulerAnglesXYZ -- This isnt necessary but I did it because it felt cool lol
function middleclass.new()
local Fractal = {}
setmetatable(Fractal, middleclass)
return Fractal
end
So now we can get started on the actual trees
- First
We have to set parameters for the function
local num = 0
local Size = 1
function middleclass:branch(height, p, deg, deg2)-- Height, The previous part, the rotation, another rotation
if height < 1 then -- We have to stop it or the game will time out
return
end
--Next we'll set some values
self.Size = Vector3.new(Size, height, Size) --Size and Height
self.Len = self.Size.y -- Length
self.CF = p.CFrame --old parts CFrame
--[[
Branch Creation
--]]
local Part = Instance.new("Part")
Part.Parent = p.Parent
Part.Anchored = true
Part.Size = self.Size
Part.Color = Color3.fromRGB(86, 66, 54)
Part.Material = Enum.Material.SmoothPlastic
end
So now we need to make sure the script does not add rotation to the trunk
local Hreturner = function()
local X = math.random(1, 100)/100
return .67 -- could be changed to x to have random branch sizes but i stuck to 67%
end
local degreturner = function(Cap)
local X = math.rad(math.random(-Cap,Cap))
return X
end
--Now i made these two functions to better control variation of rotation and sze
local num = 0 -- checks for the trunk
local Size = 1 -- branch thickness
local Cap = 45 -- maximum rotation angle
function middleclass:branch(height, p, deg, deg2)-- Height, The previous part, the rotation, another rotation
if height < 1 then -- We have to stop it or the game will time out
return
end
--Next we'll set some values
self.Size = Vector3.new(Size, height, Size) --Size and Height
self.Len = self.Size.y -- Length
self.CF = p.CFrame --old parts CFrame
--[[
Branch Creation
--]]
local Part = Instance.new("Part")
Part.Parent = p.Parent
Part.Anchored = true
Part.Size = self.Size
Part.Color = Color3.fromRGB(86, 66, 54)
Part.Material = Enum.Material.SmoothPlastic
if num == 0 then -- Checks if the current Part is the trunk
local store = self.CF * CFrame.new(0,(p.Size.Y/2+ Part.Size.Y/2),0) -- Even though this is a trunk the previous part is where it starts
Part.CFrame = store
num = num + 1 -- So it doesnt think its the trunk ever again
for d = 1,1 do -- Can be changed to make more than 2 branches
for i =1, 2 do -- makes 2 branches
spawn(function()
if i == 1 then
middleclass:branch(height * Hreturner(), Part,degreturner(Cap), -degreturner(Cap)) -- Creates a new branch calling itself
elseif i == 2 then
middleclass:branch(height * Hreturner(), Part, -degreturner(Cap), degreturner(Cap)) -- creates a new branch calling itself
end
end)
end
end
else
end
end
So now we need to handle it if its not the trunk
if num == 0 then -- Checks if the current Part is the trunk
local store = self.CF * CFrame.new(0,(p.Size.Y/2+ Part.Size.Y/2),0) -- Even though this is a trunk the previous part is where it starts
Part.CFrame = store
num = num + 1 -- So it doesnt think its the trunk ever again
for d = 1,1 do -- Can be changed to make more than 2 branches
for i =1, 2 do -- makes 2 branches
spawn(function()
if i == 1 then
middleclass:branch(height * Hreturner(), Part,degreturner(Cap), -degreturner(Cap)) -- Creates a new branch calling itself
elseif i == 2 then
middleclass:branch(height * Hreturner(), Part, -degreturner(Cap), degreturner(Cap)) -- creates a new branch calling itself
end
end)
end
end
else -- If its not the trunk then
local store3 = self.CF * CFrame.new(0,(p.Size.Y/2),0) -- gets the top surface of the previous branch
local store2 = self.CF * CFrame.new(0,(p.Size.Y/2+ Part.Size.Y), 0) -- this will be used to make leaves
local store4 = self.CF * CFrame.new(0,-(p.Size.Y/2),0) -- the inverse of Store3
local CF = store3
local S, E = pcall(function()
Part.CFrame = CF * rotate(deg2,0,deg)
end)
--[[
I ran into errors with angles so if it broke id give them new ones
--]]
if S == false then
Part.CFrame = CF * rotate(-degreturner(0),0,degreturner(0))
end
end
So now we have to move the parts up
or else it will look like this…
We have to move the parts half their size
else -- If its not the trunk then
local store3 = self.CF * CFrame.new(0,(p.Size.Y/2),0) -- gets the top surface of the previous branch
local store2 = self.CF * CFrame.new(0,(p.Size.Y/2+ Part.Size.Y), 0) -- this will be used to make leaves
local store4 = self.CF * CFrame.new(0,-(p.Size.Y/2),0) -- the inverse of Store3
local CF = store3
local S, E = pcall(function()
Part.CFrame = CF * rotate(deg2,0,deg)
end)
--[[
I ran into errors with angles so if it broke id give them new ones
--]]
if S == false then
Part.CFrame = CF * rotate(-degreturner(0),0,degreturner(0))
end
Part.CFrame = Part.CFrame:ToWorldSpace(CFrame.new(0,Part.Size.Y/2,0))
if not height > 2 then -- So now we create more branches
for d = 1,1 do
for i =1, 2 do
spawn(function()
if i == 1 then
middleclass:branch(height * Hreturner(), Part,degreturner(Cap), -degreturner(Cap))
elseif i == 2 then
middleclass:branch(height * Hreturner(), Part, -degreturner(Cap), degreturner(Cap))
end
end)
end
end
else -- if its the end branch then we make leaves
end
end
So i made a function that will make leaves
We need to put this above the branch function
middleclass.Leaf = function(CF, Color, S)
local P2 = Instance.new("Part")
P2.Anchored = true
P2.Size = Vector3.new(S,S,S)
P2.CFrame = CF * rotate(degreturner(360),degreturner(360),degreturner(360))
P2.BrickColor = Color
P2.Material = Enum.Material.Grass
P2.Parent = game.Workspace
end
Continuing from where we left off…
else -- if its the end branch then we make leaves
middleclass.Leaf(store2, BrickColor.Green(), 5)
end
end
So now that everything has been done we need to start it up
Make a script in the workspace and then put the following
local Class = require(game.ReplicatedStorage.Source.etc.Tree)
local Folder = Instance.new("Folder")
Folder.Name = "Tree"
Folder.Parent = game.Workspace
local Start = Instance.new("Part")
Start.Anchored = true
Start.Position = Vector3.new(0,0,0)
Start.Name = "Start"
Start.Parent = game.Workspace
local Fractal = Class.new()
Fractal:branch(10, Start)
Entire Script
https://raw.githubusercontent.com/Th3Pr0fessor/Ants-2.0.0/master/ReplicatedStorage/Source/etc/TreeModule.lua