What would be the best way to spawn my trees so they don't look-- off

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 :sob:

2 Likes

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)

2 Likes

Hmm I guess that is a good idea. I do need to rewrite the spacing code since it doesn’t work that well at the moment :p

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

Here’s what I did:

--!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

3 Likes

first of all, your tree system looks awesome!

from what i found online you could have some sort of chunk or grid system. each chunk will have a tree inside it like so. (brown dot = tree)

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!

2 Likes

you could do that yeah, it would end up being much more controlled that way.

2 Likes

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.

I updated the code since I did a little oopsie. Tweaked some settings and this might be a better result:

Sometimes a tree ignores the spacing rule for some reason, I’ll try to fix that.

2 Likes

And for some reason I changed the floor and its still spawning near where the tree model originally existed and is not spawning at the spawner

Would it just be better to use chunk based spawning? Much easier too

If you adjust the distance properly and let basically as many trees spawn as can fit in the area, you get something that looks like this

I tested it with some janky code lol

1 Like

Maybe. I did this in a rush though, I don’t think it would be too hard to write a code for something like this.

1 Like

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.

Could you show me how you adjusted it, min, max, etc?

Extremely simple. My janky code is below:

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.

1 Like

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

1 Like

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.

1 Like

Thank you so much it works perfectly! I need to research Stardew valley systems lol. (it has helped me twice on this project believe it or not)

1 Like