local Terrain = game.Workspace.Terrain
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
-- Configuration
local CHUNK_SIZE = 32
local RENDER_DISTANCE = 5
local NOISE_SCALE = 0.005
local TERRAIN_SCALE = 40
local WATER_LEVEL = 1
local SEED = math.random(1, 1000000)
local HOUSE_CHANCE = 0.01 -- 1% chance for a house to spawn in each chunk
local HOUSE_CLEARANCE = 5 -- Area around the house to flatten
local TREE_CHANCE = 0.05 -- 5% chance for a tree to spawn in each suitable location
local TREE_CLEARANCE = 2 -- Area around the tree to check for suitability
-- Biome Configuration
local BIOMES = {
{name = "Plains", threshold = 0.3, material = Enum.Material.Grass},
{name = "Forest", threshold = 0.6, material = Enum.Material.LeafyGrass},
{name = "Desert", threshold = 0.8, material = Enum.Material.Sand},
{name = "Snow", threshold = 1, material = Enum.Material.Snow}
}
-- Store generated chunks
local generatedChunks = {}
-- Get the House and Tree models
local houseModel = game.Workspace:FindFirstChild("House")
local treeModel = game.Workspace:FindFirstChild("Tree")
if not houseModel then
error("House model not found in Workspace")
end
if not treeModel then
error("Tree model not found in Workspace")
end
-- Improved noise function with seed and octaves
local function improvedNoise(x, z, octaves)
local total = 0
local frequency = 1
local amplitude = 1
local maxValue = 0
for i = 1, octaves do
total = total + math.noise(x * NOISE_SCALE * frequency + SEED, z * NOISE_SCALE * frequency - SEED) * amplitude
maxValue = maxValue + amplitude
amplitude = amplitude * 0.5
frequency = frequency * 2
end
return total / maxValue
end
-- Function to determine biome
local function getBiome(noise)
for _, biome in ipairs(BIOMES) do
if noise <= biome.threshold then
return biome
end
end
return BIOMES[#BIOMES]
end
-- Function to check if an area is suitable for object placement
local function isSuitableForObject(heightMap, x, z, clearance)
local minHeight = math.huge
local maxHeight = -math.huge
for dx = -clearance, clearance do
for dz = -clearance, clearance do
local hx, hz = x + dx, z + dz
if hx >= 0 and hx < CHUNK_SIZE and hz >= 0 and hz < CHUNK_SIZE then
local height = heightMap[hx][hz]
minHeight = math.min(minHeight, height)
maxHeight = math.max(maxHeight, height)
end
end
end
return minHeight > WATER_LEVEL and (maxHeight - minHeight) <= 2
end
-- Function to flatten an area for object placement
local function flattenArea(chunkX, chunkZ, x, z, height, clearance)
for dx = -clearance, clearance do
for dz = -clearance, clearance do
local worldX = chunkX * CHUNK_SIZE + x + dx
local worldZ = chunkZ * CHUNK_SIZE + z + dz
Terrain:FillBlock(CFrame.new(worldX, height / 2, worldZ), Vector3.new(1, height, 1), Enum.Material.Grass)
end
end
end
-- Function to place an object (house or tree)
local function placeObject(model, chunkX, chunkZ, x, z, height)
local worldX = chunkX * CHUNK_SIZE + x
local worldZ = chunkZ * CHUNK_SIZE + z
local newObject = model:Clone()
newObject.Parent = game.Workspace
local primaryPart = newObject:FindFirstChildWhichIsA("BasePart")
if primaryPart then
newObject.PrimaryPart = primaryPart
newObject:SetPrimaryPartCFrame(CFrame.new(worldX, height + 1, worldZ))
else
warn(model.Name .. " model does not have a suitable part to use as PrimaryPart")
newObject:Destroy()
end
end
-- Function to generate terrain for a single chunk
local function generateChunk(chunkX, chunkZ)
local chunkKey = chunkX .. "," .. chunkZ
if generatedChunks[chunkKey] then return end
local heightMap = {}
for x = 0, CHUNK_SIZE - 1 do
heightMap[x] = {}
for z = 0, CHUNK_SIZE - 1 do
local worldX = chunkX * CHUNK_SIZE + x
local worldZ = chunkZ * CHUNK_SIZE + z
local noise = improvedNoise(worldX, worldZ, 4)
local biomeNoise = improvedNoise(worldX * 0.25, worldZ * 0.25, 2)
local biome = getBiome(biomeNoise)
local height = math.floor(noise * TERRAIN_SCALE + 10)
heightMap[x][z] = height
if height > WATER_LEVEL then
Terrain:FillBlock(CFrame.new(worldX, height / 2, worldZ), Vector3.new(1, height, 1), biome.material)
else
Terrain:FillBlock(CFrame.new(worldX, WATER_LEVEL / 2, worldZ), Vector3.new(1, WATER_LEVEL, 1), Enum.Material.Water)
end
end
end
-- Randomly place a house in the chunk
if math.random() < HOUSE_CHANCE then
local attempts = 0
local maxAttempts = 10
while attempts < maxAttempts do
local x = math.random(HOUSE_CLEARANCE, CHUNK_SIZE - HOUSE_CLEARANCE - 1)
local z = math.random(HOUSE_CLEARANCE, CHUNK_SIZE - HOUSE_CLEARANCE - 1)
local height = heightMap[x][z]
if isSuitableForObject(heightMap, x, z, HOUSE_CLEARANCE) then
flattenArea(chunkX, chunkZ, x, z, height, HOUSE_CLEARANCE)
placeObject(houseModel, chunkX, chunkZ, x, z, height)
break
end
attempts = attempts + 1
end
end
-- Place trees in Plains biome
for x = 0, CHUNK_SIZE - 1 do
for z = 0, CHUNK_SIZE - 1 do
local biomeNoise = improvedNoise((chunkX * CHUNK_SIZE + x) * 0.25, (chunkZ * CHUNK_SIZE + z) * 0.25, 2)
local biome = getBiome(biomeNoise)
if biome.name == "Plains" and math.random() < TREE_CHANCE then
local height = heightMap[x][z]
if isSuitableForObject(heightMap, x, z, TREE_CLEARANCE) then
placeObject(treeModel, chunkX, chunkZ, x, z, height)
end
end
end
end
generatedChunks[chunkKey] = true
end
-- Function to update terrain around the player
local function updateTerrain(player)
local character = player.Character
if not character then return end
local position = character.PrimaryPart.Position
local chunkX = math.floor(position.X / CHUNK_SIZE)
local chunkZ = math.floor(position.Z / CHUNK_SIZE)
for x = -RENDER_DISTANCE, RENDER_DISTANCE do
for z = -RENDER_DISTANCE, RENDER_DISTANCE do
generateChunk(chunkX + x, chunkZ + z)
end
end
end
-- Clear existing terrain
Terrain:Clear()
-- Update terrain when a player's character spawns
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
wait(1)
updateTerrain(player)
end)
end)
-- Continuously update terrain as players move
local lastUpdate = 0
local UPDATE_INTERVAL = 0.5
RunService.Heartbeat:Connect(function(deltaTime)
lastUpdate = lastUpdate + deltaTime
if lastUpdate >= UPDATE_INTERVAL then
for _, player in ipairs(Players:GetPlayers()) do
updateTerrain(player)
end
lastUpdate = 0
end
end)
Changelog: 1. Added tree generation in Plains biome. 2. Implemented tree placement logic with clearance checks. 3. Refactored object placement code to be more generic (used for both houses and trees). 4. Adjusted biome detection for tree placement. 5. Added TREE_CHANCE and TREE_CLEARANCE constants for easy configuration.