Recently I have been working on a tree game and I just finished the growing system where trees can grow:
It honestly looks great imo and would also look way better with different models mixed in, but the main problem is I still need to have tree spawners so that the tree models will have a max amount of trees that can spawn, and a cooldown for trees that will spawn as well as a minimum number of trees that will spawn. But I have all that figured out, what I really am confused about is spacing between trees.
I really want the trees to be sorta random on each spawner so they look like this:
The image above was not the spawner, I placed every tree myself and as you can see it has perfect spacing and seems random enough. Trees are not too far or not too close.
How would I spawn trees like that? Because I have tried doing that and it turned out like this:
I am not even sure if the spacing worked or if it was just too random.. Does anyone have a solution for this? Does anyone know a good way to spawn trees? Any other methods other than the one I am currently using which is just pick a random point in the red spawn point? I really don’t know any good ways…
Edit: I also just realized the trees now have gaps in between them
Not sure how “perfect” this solution is, but I would suggest adding some minimum distance between trees. When a tree spawns, it checks its desired location to ensure if is more than that distance away from all existing trees. If not, it picks a new location.
(also side note but make sure you randomise the orientation of each tree too lol)
Would it be a good idea to go inside the spawn part and just add a bunch of attachments. So all trees will spawn in one of those attachments? The attachments will also have a short radius so the trees can spawn anywhere in that radius randomly for just a bit randomness/naturalness
--!strict
local ServerStorage = game:GetService("ServerStorage")
local Trees = ServerStorage.Trees:GetChildren()
local Floor = workspace.Baseplate
local AMOUNT = 10
local MAX_Y_ORIENTATION, MIN_Y_ORIENTATION = 180, 0 -- max/min orientation on the y axis
local MIN_DISTANCE = 30 -- min distance apart from each other
local SpawnedTreeCFrames = {}
function FindClosestObject(newObjectPosition: CFrame, objectPositions: {CFrame})
local currentDistance = math.huge
local currentPosition = nil
for _, position in ipairs(objectPositions) do
local distance = (position * newObjectPosition:Inverse()).Position.Magnitude
if distance < currentDistance then
currentDistance = distance
currentPosition = position
end
end
return currentPosition, currentDistance
end
for i = 1, AMOUNT do
local Tree = Trees[math.random(1, #Trees)]:Clone() :: Model
local Size = Floor.Size
local X, Y, Z = Size.X/2, (math.abs(Floor.Position.Y) + math.abs(Tree:GetExtentsSize().Y)/2), Size.Z/2
-- if u are using this on terrain,
-- you need to use raycast to find your y position.
local Offset = CFrame.new(math.random(-X, X), Y , math.random(-Z, Z))
local Orientation = CFrame.Angles(0, math.rad(math.random(MIN_Y_ORIENTATION, MAX_Y_ORIENTATION)), 0)
local CFrame = Offset * Orientation
-- spacing
local ClosestPosition, ClosestDistance = FindClosestObject(CFrame, SpawnedTreeCFrames)
if ClosestDistance < MIN_DISTANCE then
-- instead of destroying, you can create a new tree.
-- by looping until it is not too close to another tree.
Tree:Destroy()
else
table.insert(SpawnedTreeCFrames, CFrame)
Tree:PivotTo(CFrame)
Tree.Parent = workspace
end
if i == AMOUNT then -- clear table
table.clear(SpawnedTreeCFrames)
end
end
This works fine for me; to make it better you can even do random sizes
from there you can do distance checks on the neighbouring chunks to see if the tree is too near to the other tree and adjust the tree’s pivot accordingly. i’m not sure if this is the best way as i’ve never done a system like this before but that’s just what i found. hopefully it helps!
I will try this and tweak with some settings + I might even add some. I am not sure how well this will work but I will lyk. Based on the image though it looks off because there is too much space and more trees just grouped up together. I am guessing that is fixable with some settings tweaks.
If that does not work I might try using attachments with a radius which will sorta work like a chunk base grid system (but with circles) like @targetdior said.
That actually looks pretty perfect I only see one or two spots too close but that doesn’t really matter since it’s a forest and sometimes trees will get too close.
local mindistance = 20
while true do
task.wait()
local desiredposition = Vector3.new(math.random(-100,100),2,math.random(-100,100))
local partswithinmindistance = workspace:GetPartBoundsInRadius(desiredposition, mindistance)
local found = false
for i, part in partswithinmindistance do
if part.Parent == workspace.Trees then
found = true
end
end
if found == false then
local part = game.ReplicatedStorage.Part:Clone()
part.Position = desiredposition
part.Parent = workspace.Trees
end
end
All it does is try to spawn as many trees as possible in an area, while keeping that minimum distance. The script runs forever, but eventually there is no more space so no more trees spawn. If you adjust the wait timer you end up with a natural re-spawning system if your trees are being cut down which is pretty neat, otherwise you can just run the function in the while true loop a few hundred times and chances are your area will be full enough.
Obviously you’ll need to adjust how this function runs in your own code (as well as how the tree actually spawns etc.). I don’t recommend using a while true loop, it’s just for demonstration. But the logic is there and the outcome is as desired
Wow this is WAY more simple then I had it lol.. I guess I was overthinking. Your code is nice and simple and can go a long way especially with a regeneration timer with trees that get chopped. I will try this and lyk if it works.. I can’t wait to see the final product.
No worries. The logic is simple but works well. I’m pretty sure it’s the same way that Stardew Valley deals with spawning. Every so often the game just picks a location, checks if it’s free (nothing too close to it), and if it is the spawn works, otherwise nothing spawns in that time interval/attempt.