Hi. I currently have a script that spawns trees randomly but it will only work for a short while before spawning the same tree in the same place when a tree is already there. Here is the script:
while true do
wait(30)
local randomnumber = math.random(1,3)
print("Trees spawning")
if randomnumber == 1 then
local Tree1 = game.ReplicatedStorage.Trees.Tree:Clone()
Tree1.Parent = game:GetService("Workspace")
else if randomnumber == 2 then
local Tree2 = game.ReplicatedStorage.Trees.Tree2:Clone()
Tree2.Parent = game:GetService("Workspace")
else if randomnumber == 3 then
local Tree3 = game.ReplicatedStorage.Trees.Tree3:Clone()
Tree3.Parent = game:GetService("Workspace")
end
end
end
end
How do I make the trees spawn in different locations randomly and make sure a tree at point A will not spawn if there is a tree already there. And, how do I despawn trees after lets say 30 seconds after no one comes to cut them down. (This is to prevent too many trees spawning and overloading the server.)
Your issue is when you :Clone() the tree from ReplicatedStorage you don’t actually set where you want to put it inside of workspace. If your tree model is a model you could use the function SetPrimaryPartCFrame() to set the tree’s primary part to a random position. To clarify,
while true do
wait(waitTime)
local tree = game.ReplicatedStorage.Trees.Tree:Clone()
tree.Parent = workspace
if tree:IsA("Model") and tree.PrimaryPart then -- checks if the tree is a class model and that the primary part exists
tree:SetPrimaryPartCFrame(CFrame.new(RANDOMPOSITION))
end
end
You’d also want to define/create a random position for the tree to spawn using math.random to set a range of x, y, z values to spawn it between. There are other methods of doing so but this is one. Hope this helps.
Hmm, how do I use math random to set positions? Also, how do I make sure that 2 trees dont spawn in the same spot and how do I make them despawn after awhile?
You can make sure that 2 trees dont spawn in the same spot by checking if the tree is touching another tree with the Touched event. If it detects another tree, you can delete the tree or reposition it.
Let’s say you want your tree to spawn randomly between two points. Example: Trees x position is in between 100 and 200. Then the trees z position is between 10 and 50. You simply do tree.position = vector3.new(math.random(100,200),y,math.random(10,50)) and to check if it’s in the same position as another you can just set their parent inside a folder and check their positions as @happygeneral2015 said
Positioning:
Use math.random(m, n), for the X and Y coordinates of your trees, keeping m, n within the boundaries of your map. I would recommend keeping all the trees in a folder or table and then looping through that, then checking the magnitude (distance within a 3d space) from the new tree and the pre-existing trees. If it’s say >30 studs away, generate a new position (use a repeat loop).
Destroying trees after a certain time:
Use the Debris service, e.g: game:GetService("Debris"):AddItem(Tree, *Insert time here*)
For any decent number of trees this will be pretty slow, e.g. for the 100th tree that’s 99 distance checks, PLUS the chance of overlapping rises which means you have to do another 99 distance checks.
Wouldn’t it be better to only spawn new trees once some have been cut down?
You can choose to e.g. spawn trees up to 30, then stop spawning until the number gets below 30, etc.
Summary
local treeTypes = game.ServerStorage.TreeTypes:GetChildren()
local maxTrees = 30
local spawnedTrees = {}
function chooseRandomTree()
return treeTypes[math.random(1, #treeTypes)]
end
function spawnTree()
local tree = chooseRandomTree():Clone()
tree:SetPrimaryPartCFrame(CFrame.new(
math.random(-100, 100),
0,
math.random(-100, 100)
))
table.insert(spawnedTrees, tree)
tree.Parent = game.Workspace
local treeCutDownEvent = tree:FindFirstChild("CutDown")
local c
c = treeCutDownEvent.Event:Connect(function()
c:Disconnect()
despawnTree(tree)
end)
end
function despawnTree( tree )
table.remove( spawnedTrees, table.find(spawnedTrees, tree) ) --all new trees to be spawned
Debris:AddItem(tree, 5) --remove the model after a few seconds
end
while wait(1) do
if #spawnedTrees < maxTrees then
spawnTree()
end
end
To prevent trees from overlapping, I would keep track of every possible position a tree can be in and if that position is already taken. To limit the possible positions, I’d place trees on a grid. To prevent it from looking like a grid, I’d place the trees at a grid position but offset a small random amount.
Summary
local treeTypes = game.ServerStorage.TreeTypes:GetChildren()
local GRID_EXTENTS = {Min = Vector2.new(-100, -100), Max = Vector2.new(100, 100)}
local GRID_SPACING = 10
local CHOOSE_RANDOM_MAX_TRIES = 10
local random = Random.new()
local maxTrees = 30
local spawnedTrees = {}
local treeMap = {}
function mapGet( map, x, y )
if map[x] then
return map[x][y]
end
return nil
end
function mapSet( map, x, y, value )
map[x] = map[x] or {}
map[x][y] = value
end
function round( n, to )
return math.floor(n/to + 0.5) * to
end
function chooseRandomTree()
return treeTypes[random:NextInteger(1, #treeTypes)]
end
function chooseRandomGridPosition( extents )
local gridX, gridY =
round( random:NextInteger(extents.Min.X, extents.Max.X), 1 ),
round( random:NextInteger(extents.Min.Y, extents.Max.Y), 1 )
end
function chooseRandomEmptyGridPosition( extents )
local chosenPos
for _ = 1, CHOOSE_RANDOM_MAX_TRIES do
local randomPos = chooseRandomGridPosition( extents )
local treeAtPos = mapGet(treeMap, randomPos.X, randomPos.Y)
if not treeAtPos then
chosenPos = randomPos
break
end
end
return chosenPos
end
function spawnTree()
local treeGridPos = chooseRandomGridPosition(GRID_EXTENTS)
if treeGridPos then
local tree = chooseRandomTree():Clone()
tree:SetPrimaryPartCFrame(
CFrame.new() + Vector3.new(
treeGridPos.X + random:NextNumber(-0.5, 0.5),
0,
treeGridPos.Y + random:NextNumber(-0.5, 0.5)
) * GRID_SPACING
)
tree.GridPosValue.Value = treeGridPos
table.insert(spawnedTrees, tree)
mapSet(treeMap, treeGridPos.X, treeGridPos.Y, tree)
tree.Parent = game.Workspace
local treeCutDownEvent = tree:FindFirstChild("CutDown")
local c
c = treeCutDownEvent.Event:Connect(function()
c:Disconnect()
despawnTree(tree)
end)
return tree
else
return nil
end
end
function despawnTree( tree )
table.remove( spawnedTrees, table.find(spawnedTrees, tree) ) --all new trees to be spawned
local treeGridPos = tree.GridPosValue.Value
mapSet(treeMap, treeGridPos.X, treeGridPos.Y, nil)
Debris:AddItem(tree, 5) --remove the model after a few seconds
end
while wait(1) do
if #spawnedTrees < maxTrees then
spawnTree()
end
end
I have published a demo place to help you out, feel free to copy its open to all. Just a quick knock up. Its using a Unioned Part as the tree as per OP requirements.