Fractal Trees in roblox

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.
    40%20AM

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

6 Likes

This topic was automatically closed after 1 minute. New replies are no longer allowed.