I’m working on a system that generates islands that have different sizes. Generating them was easy enough, it’s just the matter of placing objects randomly throughout the island. While working on the script, however, I noticed that sometimes, trees and rocks may generate on top of one-another, even though I have a minimum distance check and I also add the positions of generated objects to tables, etc.
I will provide code and a place file since there’s a decent chunk of code here, some of you may want to look through it and help me find the problem.
Also, if there’s anything I can improve on besides all the bugs then please let me know! Thanks.
DecorationPlacer module
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local treePositions = {}
local rockPositions = {}
local tree = ReplicatedStorage.Tree
local rock = ReplicatedStorage.Rock
local MAX_TREES = 100 -- Maximum number of trees to generate on the island
local MAX_ROCKS = 100 -- Maximum number of rocks to generate on the island
local MIN_DISTANCE = 10 -- Minimum distance apart from each tree/rock. A new one will not generate if there's another within this distance
local module = {}
function module.CountTrees(folder)
return #folder:GetChildren()
end
function module.CountRocks(folder)
return #folder:GetChildren()
end
function module.FindClosestObject(newObjectPosition, objectPositions)
local currentDistance = math.huge
local currentPosition = nil
for _, position in ipairs(objectPositions) do
local distance = (position - newObjectPosition).Magnitude
if distance < currentDistance then
currentDistance = distance
currentPosition = position
end
end
return currentPosition, currentDistance -- Return both the closest position and the distance
end
function module.PlaceDecoration(decorationModel, decorationPositions, folder, part)
local decorationClone = decorationModel:Clone()
decorationClone.Parent = folder
local landSizeX = part.Size.X
local landSizeZ = part.Size.Z
local offsetX = landSizeX / 2
local offsetZ = landSizeZ / 2
local minX = -offsetX + MIN_DISTANCE
local maxX = offsetX - MIN_DISTANCE
local minZ = -offsetZ + MIN_DISTANCE
local maxZ = offsetZ - MIN_DISTANCE
local randomPosX = math.random(minX, maxX)
local randomPosZ = math.random(minZ, maxZ)
local newPosition = Vector3.new(randomPosX, 0, randomPosZ) -- Set Y position to 0 initially
-- Perform a raycast to find the correct Y position on the terrain surface
local raycastResult = workspace:Raycast(newPosition + Vector3.new(0, 100, 0), Vector3.new(0, -100, 0))
if raycastResult then
local terrainSurfaceY = raycastResult.Position.Y
newPosition = Vector3.new(newPosition.X, terrainSurfaceY, newPosition.Z)
end
if #decorationPositions > 0 then
local closestDecorationPosition, closestDistance = module.FindClosestObject(newPosition, decorationPositions)
if closestDecorationPosition and closestDistance < MIN_DISTANCE then
decorationClone:Destroy()
else
decorationClone.PrimaryPart.CFrame = CFrame.new(newPosition) * CFrame.Angles(0, math.rad(math.random(0, 360)), 0)
table.insert(decorationPositions, decorationClone.PrimaryPart.Position)
end
else
decorationClone.PrimaryPart.CFrame = CFrame.new(newPosition) * CFrame.Angles(0, math.rad(math.random(0, 360)), 0)
table.insert(decorationPositions, decorationClone.PrimaryPart.Position)
end
end
function module.PlaceDecorationTypes(treesFolder, rocksFolder, part)
if module.CountTrees(treesFolder) >= MAX_TREES and module.CountRocks(rocksFolder) >= MAX_ROCKS then
return
end
local decorationType = math.random(1, 2)
if decorationType == 1 and module.CountTrees(treesFolder) < MAX_TREES then
module.PlaceDecoration(tree, treePositions, treesFolder, part)
elseif decorationType == 2 and module.CountRocks(rocksFolder) < MAX_ROCKS then
module.PlaceDecoration(rock, rockPositions, rocksFolder, part)
end
end
return module
IslandGenerator module
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local SandPartTemplate = ReplicatedStorage.Sand
local DirtPartTemplate = ReplicatedStorage.Dirt
local GrassPartTemplate = ReplicatedStorage.Grass
local decorationPlacer = require(script.Parent.DecorationPlacer)
local module = {}
local function CreatePart(template, position, size, parent)
local part = template:Clone()
part.Position = position
part.Size = size
part.Parent = parent
return part
end
function module.GenerateIsland(sizeX, sizeZ, sandHeight, dirtHeight, grassHeight, parent)
local islandTemplate = script.Island:Clone()
islandTemplate.Parent = workspace.Islands.Generated
local sandPart = CreatePart(SandPartTemplate, Vector3.new(0, 0, 0), Vector3.new(sizeX + 20, sandHeight, sizeZ + 20), islandTemplate)
local dirtPosition = sandPart.Position + Vector3.new(0, sandPart.Size.Y / 2 + dirtHeight / 2, 0)
local dirtPart = CreatePart(DirtPartTemplate, dirtPosition, Vector3.new(sizeX, dirtHeight, sizeZ), islandTemplate)
local grassPosition = dirtPart.Position + Vector3.new(0, dirtPart.Size.Y / 2 + grassHeight / 2, 0)
local grassPart = CreatePart(GrassPartTemplate, grassPosition, Vector3.new(sizeX + 2, grassHeight, sizeZ + 2), islandTemplate)
for i = 1, 12 do
decorationPlacer.PlaceDecorationTypes(islandTemplate.Trees, islandTemplate.Rocks, grassPart)
end
return sandPart, dirtPart, grassPart
end
return module
WIP.rbxl (63.1 KB)