I am struggling to create trees in a randomly generated world

The thing I am doing is generating trees by creating a raycast into the sky, so that way nothing obstructs the tree for a realystic generation, but the raycast ignores blocks and makes the trees clip into mountains and hills.

local BLOCK_SIZE = 3.054
local MAP_SIZE = Vector3.new(100, 64, 100)
local MOUNTAIN_INTENSITY = 8

seed = workspace.SEED.Value
noisescale = 1/64
amplitude = 10

function get_density(v3)
	local noise_value = math.noise(v3.X * noisescale, v3.Y * noisescale, v3.Z * noisescale + seed)*amplitude
	local height_bonus = -v3.Y / MOUNTAIN_INTENSITY + 8

	return noise_value + height_bonus
end

function is_on_boundary(v3)
	if get_density(v3) <= 0 then
		return false
	end

	for _, dirEnum in pairs(Enum.NormalId:GetEnumItems()) do
		local dir = Vector3.FromNormalId(dirEnum)
		local neighbor_pos = v3 + dir * BLOCK_SIZE
		local neighbor_density = get_density(neighbor_pos)

		if neighbor_density <= 0 then
			return true
		end
	end

	return false
end

for x = 0, MAP_SIZE.X do
	for y = 0, MAP_SIZE.Y do
		for z = 0, MAP_SIZE.Z do
			local block_pos = Vector3.new(x, y, z) * BLOCK_SIZE
			local density = get_density(block_pos)

			if density > 0 and is_on_boundary(block_pos) then
				local block = script.Block:Clone()
				block.Parent = game.Workspace
				block.CFrame = CFrame.new(block_pos)
				if y > 25 then
					local c = game.ReplicatedStorage.Blocks.Nature.Snow:Clone()
					c:SetPrimaryPartCFrame(block.CFrame)
					c.Parent = workspace.TerrainFolder
					block:Destroy()
				elseif y < 23 and y > 10 then
					local c = game.ReplicatedStorage.Blocks.Nature.Grass:Clone()
					c:SetPrimaryPartCFrame(block.CFrame)
					c.Parent = workspace.TerrainFolder
					block:Destroy()
					
					local startPos = c.Grass.Position
					local finishPos = Vector3.new(c.Dirt.Position.X, c.Dirt.Position.Y + 10000, c.Dirt.Position.Z) 
					
					local rayDirection = finishPos - startPos
					
					local trees = game.ReplicatedStorage.Structures.Trees:GetChildren()
					
					local ray = Ray.new(startPos, rayDirection)

					local hit = workspace:Raycast(startPos, finishPos)
					
					if math.random(1, 150) == 1 and hit == nil then
						local tree = trees[math.random(1, #trees)]:Clone()
						tree.Parent = workspace.TerrainFolder.Trees
						tree:SetPrimaryPartCFrame(c.PrimaryPart.CFrame)
					end
					
					if hit == nil then
						print("Nothing obstructed the tree!")
					else
						print(hit.Position)
					end
					
				elseif y <= 10 then
					local p = Instance.new("Part")
					p.Parent = workspace.TerrainFolder
					p.Size = Vector3.new(3.054, 0.005, 3.054)
					p.Anchored = true
					p.CanCollide = false
					p.Transparency = 0.01
					p.Reflectance = 0.1
					p.CFrame = CFrame.new(block.CFrame.X, 30, block.CFrame.Z)
					p.Material = Enum.Material.SmoothPlastic
					p.Name = "Water"
					p.BrickColor = BrickColor.new("Electric blue")
					
					local c = game.ReplicatedStorage.Blocks.Nature.Sand:Clone()
					c:SetPrimaryPartCFrame(block.CFrame)
					c.Parent = workspace.TerrainFolder
					block:Destroy()
				else
					local c = game.ReplicatedStorage.Blocks.Nature.Stone:Clone()
					c:SetPrimaryPartCFrame(block.CFrame)
					c.Parent = workspace.TerrainFolder
					block:Destroy()
				end
			end
			
		end
	end
	wait(0.2)
end

Anything I could do? Something to fix?

check if the region3 of where the tree will go would work, if it returns other land then dont add the tree
Region3 (roblox.com)

Elaborate please.⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

ok ill try, so the basic idea is that you would first create a region 3, and a hitbox for the tree though it is possible without its just harder. you could also use this but region 3 is faster and doesnt require a part being in the workspace.

local Point1 = Hitbox.Position - Hitbox.Size -- im not great with region3 so this may be wrong
local Point2 = Hitbox.Position + Hitbox.Size
local Region = Region3.new(Point1,Point2)
local touching = game.Workspace:FindPartsInRegion3(Region,Hitbox,math.huge) -- table with parts in region3, ignoring hitbox

you would then loop through the touching to see if its touching something that it shouldnt.

Info and stuff from

1 Like