Hello! Here is my first test for procedural terrain generation using math.noise().
Map is approx. 3,200 x 3,200 studs!
(Islands were an accident, but worked out quite well!)
Any tips? Is there an easy-ish way to replicate this with actual Voxel terrain? Is there a way to make mountains instead of random hills, without making things too complex? Let me know any suggestions!
(My code if anyone wants to improve it or use it):
local voxelSize = 16
local chunkSize = 64
local edgeX = chunkSize/voxelSize
local edgeZ = chunkSize/voxelSize
local biomeSize = 16
local heightFactor = 1.5
local threshold = 16
local baseHeight = 25 + (heightFactor)^2
local centerPoint = Vector3.new(0,baseHeight,0)
local vs = voxelSize
local materials = {
Sand = {Color=Color3.fromRGB(234,184,146),Material=Enum.Material.Sand,HeightOffset=1},
Grass = {Color=Color3.fromRGB(84,120,46),Material=Enum.Material.Grass,HeightOffset=0},
Dirt = {Color=Color3.fromRGB(108,88,75),Material=Enum.Material.Slate,HeightOffset=0},
Stone = {Color=Color3.fromRGB(91,93,105),Material=Enum.Material.Concrete,HeightOffset=4},
Water = {Color=Color3.fromRGB(82,124,174),Material=Enum.Material.Foil,HeightOffset=-4},
}
local biomes = {
Plain={Weight=8,HeightFactor=1,Material=materials.Grass},
Desert={Weight=5,HeightFactor=1,Material=materials.Sand},
Volcano={Weight=1,HeightFactor=1.25,Material=materials.Stone},
Ocean={Weight=4,HeightFactor=.75,Material=materials.Water},
}
function adjust(part)
local y1 = part.Position.Y
part.Size = Vector3.new(vs,y1,vs)
part.Position = part.Position + Vector3.new(0,-y1/2,0)
end
function makePart(chunk,x,y,z,material)
local part = Instance.new("Part")
part.TopSurface = Enum.SurfaceType.Smooth; part.BottomSurface = Enum.SurfaceType.Smooth
part.Anchored = true
part.Parent = chunk
part.Position = Vector3.new(x*vs,y*vs*heightFactor,z*vs) + centerPoint - Vector3.new(voxelSize/2,0,voxelSize/2)
part.Size = Vector3.new(vs,vs,vs)
adjust(part)
part.Color = material.Color; part.Material = material.Material
part.Position = part.Position + Vector3.new(0,material.HeightOffset,0)
end
function makeChunk(biomeType,chunkX,chunkZ)
local chunk = Instance.new("Folder")
chunk.Parent = workspace
chunk.Name = "Chunk"..chunkX.."/"..chunkZ
centerPoint = Vector3.new(chunkX*chunkSize,baseHeight,chunkZ*chunkSize)
local baseMaterial = biomes[biomeType].Material
local check1, check2 = false, false
coroutine.wrap(function()
for x = 1-edgeX/2,edgeX/2 do
for z = 1-edgeZ/2,0 do
local trueX, trueZ = x+centerPoint.X/voxelSize, z+centerPoint.Z/voxelSize
makePart(chunk,x,math.noise(trueX/5,(trueX+trueZ)/10,trueZ/5),z,baseMaterial)
end
if x%threshold==0 then wait() end
end
check1 = true
end)()
coroutine.wrap(function()
for x = edgeX/2,1-edgeX/2,-1 do
for z = edgeZ/2,1,-1 do
local trueX, trueZ = x+centerPoint.X/voxelSize, z+centerPoint.Z/voxelSize
makePart(chunk,x,math.noise(trueX/5,(trueX+trueZ)/10,trueZ/5),z,baseMaterial)
end
if x%threshold==0 then wait() end
end
check2 = true
end)()
repeat wait() until check1 and check2
end
local currentBiomeName = "Plain"
function setBiome(name)
heightFactor = biomes[name].HeightFactor
currentBiomeName = name
end
function getTotalWeight()
local val = 0
for i,v in pairs(biomes) do
val = val + v.Weight
end
local count = 0
for i,v in pairs(biomes) do
v.Threshold = count + v.Weight/val
count = v.Threshold
end
return val
end
local totalBiomeWeight = getTotalWeight()
function getWeightedBiome(noise)
noise = math.clamp(noise,0,1)
--print("NOISE:"..noise)
local biome = "Plain"
for i,v in pairs(biomes) do
if noise<v.Threshold then
biome=i
--print("BIOME SET:"..biome)
break
end
end
return biome
end
function getBiome(x,z)
local noise = math.noise(x*math.sqrt(2),z*math.sqrt(2)) + .5
return getWeightedBiome(noise)
end
function round(input,digits)
return (input*10^digits)/10^digits
end
for x = -15,15 do
for z = -15,15 do
setBiome(getBiome(x/biomeSize,z/biomeSize))
makeChunk(currentBiomeName,x,z)
end
end