I’m trying to make clouds with 4D perlin noise but I’ve encountered a bug. When testing it after a long time the parts just created sloped columns of parts instead of noise. Heres my clouds:
vs how i want it:
Here’s my module that makes perlin noise: `local PerlinNoiseAPI = {}
function PerlinNoiseAPI.new(coords,amplitude,octaves,persistence)
coords = coords or {}
octaves = octaves or 1
persistence = persistence or 0.5
if #coords > 4 then
error(“The Perlin Noise API doesn’t support more than 4 dimensions!”)
else
if octaves < 1 then
error(“Octaves have to be 1 or higher!”)
else
local X = coords[1] or 0
local Y = coords[2] or 0
local Z = coords[3] or 0
local W = coords[4] or 0
amplitude = amplitude or 10
octaves = octaves-1
if W == 0 then
local perlinvalue = (math.noise(X/amplitude,Y/amplitude,Z/amplitude))
if octaves ~= 0 then
for i = 1,octaves do
perlinvalue = perlinvalue+(math.noise(X/(amplitude*(persistence^i)),Y/(amplitude*(persistence^i)),Z/(amplitude*(persistence^i)))/(2^i))
end
end
return perlinvalue
else
local AB = math.noise(X/amplitude,Y/amplitude)
local AC = math.noise(X/amplitude,Z/amplitude)
local AD = math.noise(X/amplitude,W/amplitude)
local BC = math.noise(Y/amplitude,Z/amplitude)
local BD = math.noise(Y/amplitude,W/amplitude)
local CD = math.noise(Z/amplitude,W/amplitude)
local BA = math.noise(Y/amplitude,X/amplitude)
local CA = math.noise(Z/amplitude,X/amplitude)
local DA = math.noise(W/amplitude,X/amplitude)
local CB = math.noise(Z/amplitude,Y/amplitude)
local DB = math.noise(W/amplitude,Y/amplitude)
local DC = math.noise(W/amplitude,Z/amplitude)
local ABCD = AB+AC+AD+BC+BD+CD+BA+CA+DA+CB+DB+DC
local perlinvalue = ABCD/12
if octaves ~= 0 then
for i = 1,octaves do
local AB = math.noise(X/(amplitude*(persistence^i)),Y/(amplitude*(persistence^i)))
local AC = math.noise(X/(amplitude*(persistence^i)),Z/(amplitude*(persistence^i)))
local AD = math.noise(X/(amplitude*(persistence^i)),W/(amplitude*(persistence^i)))
local BC = math.noise(Y/(amplitude*(persistence^i)),Z/(amplitude*(persistence^i)))
local BD = math.noise(Y/(amplitude*(persistence^i)),W/(amplitude*(persistence^i)))
local CD = math.noise(Z/(amplitude*(persistence^i)),W/(amplitude*(persistence^i)))
local BA = math.noise(Y/(amplitude*(persistence^i)),X/(amplitude*(persistence^i)))
local CA = math.noise(Z/(amplitude*(persistence^i)),X/(amplitude*(persistence^i)))
local DA = math.noise(W/(amplitude*(persistence^i)),X/(amplitude*(persistence^i)))
local CB = math.noise(Z/(amplitude*(persistence^i)),Y/(amplitude*(persistence^i)))
local DB = math.noise(W/(amplitude*(persistence^i)),Y/(amplitude*(persistence^i)))
local DC = math.noise(W/(amplitude*(persistence^i)),Z/(amplitude*(persistence^i)))
local ABCD = AB+AC+AD+BC+BD+CD+BA+CA+DA+CB+DB+DC
perlinvalue = perlinvalue+((ABCD/12)/(2^i))
end
end
return perlinvalue
end
end
end
end
return PerlinNoiseAPI
And finally, this is my server script that activates the perlin noise module:local module = require(script.PerlinNoise)
local cloudFolder = Instance.new(“Folder”)
cloudFolder.Name = “Clouds”
cloudFolder.Parent = workspace
local BOUNDARY = 32
local Y_MIN = 0
local Y_MAX = 8
local FADE_RANGE = 6
local WIND = Vector3.new(0.2, 0, 0.1)
local SPAWN_INTERVAL = 5
local function getEdgeTransparency(x, z)
local distX = math.min(x + BOUNDARY, BOUNDARY - x)
local distZ = math.min(z + BOUNDARY, BOUNDARY - z)
local edgeDist = math.min(distX, distZ)
return math.clamp(1 - (edgeDist / FADE_RANGE), 0, 1)
end
local function createPart(x, y, z)
local g = Instance.new(“Part”)
g.Parent = cloudFolder
g.Size = Vector3.new(1.5, 1.5, 1.5)
g.Position = Vector3.new(x, y, z)
g.Anchored = true
g.Material = Enum.Material.SmoothPlastic
g.Color = Color3.new(1, 1, 1)
g.CanCollide = false
g.CastShadow = false
g.Transparency = getEdgeTransparency(x, z)
return g
end
– Generate initial cloud field once
local cloudParts = {}
for x = -BOUNDARY, BOUNDARY do
for y = Y_MIN, Y_MAX do
for z = -BOUNDARY, BOUNDARY do
local noise = module.new({x, y, z, 0}, 12, 4, 0.5)
if noise > .2 then
table.insert(cloudParts, createPart(x, y, z))
end
end
end
end
local windOffsetX = 0
local windOffsetZ = 0
local frame = 0
while true do
frame += 1
windOffsetX += WIND.X
windOffsetZ += WIND.Z
for i = #cloudParts, 1, -1 do
local part = cloudParts[i]
if not part or not part.Parent then
table.remove(cloudParts, i)
else
local newPos = part.Position + WIND
if newPos.X > BOUNDARY or newPos.X < -BOUNDARY or newPos.Z > BOUNDARY or newPos.Z < -BOUNDARY then
part:Destroy()
table.remove(cloudParts, i)
else
part.Position = newPos
part.Transparency = getEdgeTransparency(newPos.X, newPos.Z)
end
end
end
if frame % SPAWN_INTERVAL == 0 then
for y = Y_MIN, Y_MAX do
for z = -BOUNDARY, BOUNDARY do
local noise = module.new({-BOUNDARY + windOffsetX, y, z + windOffsetZ, 0}, 12, 4, 0.5)
if noise > .2 then
table.insert(cloudParts, createPart(-BOUNDARY, y, z))
end
end
end
-- Entry at -Z edge (skip corner already covered by -X)
for y = Y_MIN, Y_MAX do
for x = -BOUNDARY + 1, BOUNDARY do
local noise = module.new({x + windOffsetX, y, -BOUNDARY + windOffsetZ, 0}, 12, 4, 0.5)
if noise > .2 then
table.insert(cloudParts, createPart(x, y, -BOUNDARY))
end
end
end
end
task.wait(0.1)
end`
the module script is a child of the server script

