Using perlin noise for cave generation?

So I’ve recently acquired a script that creates Perlin worms via 3D Perlin noise
question is, how would I go about using these Perlin worms to create caves?
I’ve tried to use a heavily modified 2D Perlin noise system to do so and that obviously didn’t work too well.
Code:

local Seed = tick()
local Resolution = 4
local NumWorm = 1

while NumWorm < 50 do
	NumWorm = NumWorm + 1
	local sX = math.noise(NumWorm/Resolution + 0.1,Seed)
	local sY = math.noise(NumWorm/Resolution + sX + 0.1,Seed)
	local sZ = math.noise(NumWorm/Resolution + sY + 0.1,Seed)
	local WormCF = CFrame.new(sX * 500,sY * 500,sZ * 500)
	local Dist = (math.noise(NumWorm/Resolution + WormCF.p.magnitude,Seed) + 0.5) * 500
	for i = 1,Dist do
		local X,Y,Z = math.noise(WormCF.X/Resolution + 0.1,Seed),math.noise(WormCF.Y/Resolution + 0.1,Seed),math.noise(WormCF.Z/Resolution + 0.1,Seed)
		WormCF = WormCF*CFrame.Angles(X * 2,Y * 2,Z * 2)*CFrame.new(0,0,-Resolution)
	end
end
2 Likes

I’ve had success in the past with this approach:

Step along each segment of the worm in steps of 1 block, removing blocks within a sphere centered on the current step.

1 Like

Could you go more into detail with this approach?
I apologize if this sounds rude but your description of said approach is a bit too vague for me.

1 Like

No problem. Say you’ve generated a list of points along the worm indicating the endpoints of the line segments that make up the worm. You can turn that into a list of points that are at most one block away from each other like this:

local sphereCenters = {}
for i = 2, #wormEndPoints do
    local prevPoint = wormEndPoints[i - 1]
    local currentPoint = wormEndPoints[i]
    local dir = (currentPoint - prevPoint).Unit
    local dist = (currentPoint - prevPoint).Magnitude
    for j = 0, dist, blockSize do
       local sphereCenter = prevPoint + dir * j
       table.insert(sphereCenters, sphereCenter)
    end
end

This gives you the centers of each sphere where blocks should be removed. You can do that like so:

function setBlockTypeInSphere(blockTypeID, center, radius)
    for x = center.X - radius, center.X + radius do
        for y = center.Y - radius, center.Y + radius do
            for z = center.Z - radius, center.Z + radius do
                setBlockTypeAtPoint(blockTypeID, Vector3.new(x, y, z)) --Or however you turn blocks into air
            end
        end
    end
end

for _, sphereCenter in pairs(sphereCenters) do
    setBlockTypeInSphereCenter(AIR_BLOCK_TYPE_ID, sphereCenter, WORM_RADIUS)
end

Hope this helps, feel free to ask if you have any other questions :slight_smile:

1 Like

Hey I know I’m a little off topic but where can I learn some basics of this kind of terrain generation? It seems interesting to me

look up “Perlin noise” on google

1 Like

I’ve been struggling to create a terrain generator using Roblox’s Smooth Terrain. I looked at the source code for the terrain plugin, but it’s so convoluted and uses a bunch of library and resources that would take me a few months to read through and understand. Do you have any advice for learning how to generate terrain that won’t make my head explode?

1 Like

So after trying to tweak a lot of things and change a lot of connections I’m still left with blocks just spawning almost inside other blocks
As you can tell I really don’t know how to put this all to together in a proper manner
Code:

local Seed = tick()
local Resolution = 4
local NumWorm = 1
local worms = {}

local spherecenters = {}

while NumWorm < 50 do
	NumWorm = NumWorm + 1
	local sX = math.noise(NumWorm/Resolution + 4,Seed)
	local sY = math.noise(NumWorm/Resolution + sX + 4,Seed)
	local sZ = math.noise(NumWorm/Resolution + sY + 4,Seed)
	local WormCF = CFrame.new(sX * 500,sY * 500,sZ * 500)
	local Dist = (math.noise(NumWorm/Resolution + WormCF.p.magnitude,Seed) + 0.5) * 500
	for i = 1,Dist do
		local X,Y,Z = math.noise(WormCF.X/Resolution + 4,Seed),math.noise(WormCF.Y/Resolution + 4,Seed),math.noise(WormCF.Z/Resolution + 4,Seed)
		WormCF = WormCF*CFrame.Angles(X * 2,Y * 2,Z * 2)*CFrame.new(0,0,-Resolution)
		table.insert(worms,WormCF.p)
	end
end

for i = 2, #worms do
	local prevPoint = worms[i - 1]
	local currentPoint = worms[i]
	local dir = (currentPoint - prevPoint).Unit
	local dist = (currentPoint - prevPoint).Magnitude
	for j = 0, dist,1 do
		local spherecenter = prevPoint + dir * j
		table.insert(spherecenters,spherecenter)
	end
end

local function setblock(ID,pos)
	local s,e = pcall(function()
		if ID == 0 then
			local block = Instance.new("Part")
			block.Anchored = true
			block.Size = Vector3.new(4,4,4)
			block.Position = pos
			block.Parent = workspace
		elseif ID == 1 then
			local block = Instance.new("Part")
			block.Anchored = true
			block.Size = Vector3.new(4,4,4)
			block.Position = pos
			block.Parent = workspace
		elseif ID == 2 then
			local region = Region3.new(pos,Vector3.new(4,4,4))
			region = region:ExpandToGrid(4)
			workspace:WaitForChild("Terrain"):FillRegion(region,4,Enum.Material.Water)
		end
	end)
	if not s then
		if e then
			warn(e .. " (terrain)")
		else
			warn("Unkown or unexpected error (terrain?)")
		end
	end
	wait()
end

function settype(blockTypeID, center, radius)
	for x = center.X - radius, center.X + radius do
		for y = center.Y - radius, center.Y + radius do
			for z = center.Z - radius, center.Z + radius do
				setblock(blockTypeID, Vector3.new(x, y, z))
			end
		end
	end
end

for _, sphereCenter in pairs(spherecenters) do
	settype(0,sphereCenter,1)
end

(Ignore the ID 0 and ID 1 thing)
(Thats leftover from an “air” type for “blocks”)