Random placement script issues

I’m working on a system for my Backrooms game to place parts randomly across an area. It’s almost complete, however the code to prevent parts from intersecting doesn’t work and the function to clear the parts freezes studio (Aside from that it does work though)
Here’s my code:

bases = {}
basesFolder = game.Workspace.bases
spawnsFolder = game.Workspace.spawn
-- Self explanatory variables
spawns = {}
minCount = 0 -- These 2 refer to minimum and maximum placements
maxCount = 0 -- These 2 refer to minimum and maximum placements
partCount = 0 -- Total parts in the folder, don't modify this
spawnedParts = {} -- FOlder to store spawned parts for debug purposes.

for i, v in ipairs(spawnsFolder:GetChildren()) do
	spawns[i] = v
	partCount += 1
	print("Part ", i, "was added to list")
end
-- Add all the objects to spawn into a list
for i, v in ipairs(basesFolder:GetChildren()) do
	bases[i] = v
	print("Spawn base ", i, "was added to list")
end
-- Add all the spawn bases into a list
function randPlace(rotation, minRotation, maxRotation)
	for i, v in bases do
		minCount = v.minValue.Value
		maxCount = v.maxValue.value
		-- Get minimum and maximum spaned part counts.
		maxX = v.Position.X + v.Size.X / 2
		minX = v.Position.X - v.Size.X / 2
		maxZ = v.Position.Z + v.Size.Z / 2
		minZ = v.Position.Z - v.Size.Z / 2
		-- Determines the edges of the base to prevent parts from generating outside
		for i=1, math.random(minCount, maxCount) do
			print(i)
			-- Test print statement
			place = spawns[math.random(1, partCount)]:Clone()
			place.Parent = game.Workspace.spawned
			maxX -= place.Size.X
			minX += place.Size.X
			maxZ -= place.Size.Z
			minZ += place.Size.Z
			-- Modify edges to prevent spawned parts from sticking out
			-- Make a new random part
			place.Position = Vector3.new(math.random(minX, maxX), place.Size.Y / 2 + v.Size.Y / 2, math.random(minZ, maxZ))
			print(place.Position)
			-- Debug print statement
			if rotation then
				place.Orientation = Vector3.new(0, math.random(minRotation, maxRotation), 0)
			end -- Randomise rotation if it's enabled
			place.Anchored = true
			spawnedParts[i] = place
			-- Add the new part to spawned parts, this lets the deletion function find it.
			place.Touched:Connect(function(collision)
				repeat
					place.Position = Vector3.new(math.random(minX, maxX), place.Size.Y / 2 + v.Size.Y / 2, math.random(minZ, maxZ))
				until collision == not game.Workspace.spawned:GetChildren()
			end) -- make that thing work later
			maxX += place.Size.X
			minX -= place.Size.X
			maxZ += place.Size.Z
			minZ -= place.Size.Z
			-- Reset edges
		end
	end
end

function reset()
	for i in spawnedParts do
		spawnedParts[i]:Destroy()
	end
end

At the end of the script I also have it call the functions for debug purposes.

For the no intersecting feature I suggest to use GetBoundingBox of the models that you are cloning, in order to know how much space occupy, then, get a CFrame coordinate from your random function that is choosing a placement point within the base boundaries, then by using GetPartBoundsInBox you will use that CFrame coordinate and the BoundingBox size you got from the model to check that it can be placed without clipping with other parts within the same space, if false, retry, until the model is placed in a enough free space. Perfectly compatible with that the feature you want to include which could reorient the cloned models.

And the reason why studio freezes and a inreal game will freeze too, its because you are deleting many parts too fast. Just a add a task.wait() line on the iteration so it has enough time to complete the task, it will increase the total time a little but ensures there is no lag/freeze in game or studio

Nope, it works perfectly fine in-game. It only freezes in studio.

Then for the freezing could be any other thing in your approach.
What I can clearly see is that deleting many parts in a fast loop without task.wait() will cause freezing. Supposing you are deleting a bunch of parts

function reset()
	for i in spawnedParts do
		spawnedParts[i]:Destroy()
        task.wait() -- this should help to avoid the freeze
	end
end

Could you explain this in more detail?

Now it returns this error:
image
The code I added is

box = place:GetBoundingBox() -- This is what's returning the error
			repeat
				place.Position = Vector3.new(math.random(minX, maxX), place.Size.Y / 2 + v.Size.Y / 2, math.random(minZ, maxZ))
			until box:GetPartBoundsInBox() == {}

What is “place”?. GetBoudingBox() is to measure the size that a MODEL occupies, is “place” a model?
did you check the docu?

EDIT: my bad I didnt saw the screenshot…

SO, its a PART, GetBoundingBox is inteded for a Model not a part

Well then what should I do? Should I just turn the parts into individual models or is there another method I can use?

Yeah,make the parts into models

Because PLACEMTN SYSETEM ONLY WORKS WHEN U HAVE MODELS

Well now it says that :GetPartBoundsInBox() is not a valid member of the model.

try i:Desroy()

strong text

I already fixed the issue there, now the issue is with preventing part intersection.

what is the issue now?
strong text

Be sure to read the documentation… GetPartBoundsInBox() doesnt use a model.
You provide coordinates to that, the idea is to check if there is parts in that coordinate and box size.

Once you check if there are parts in there or not, you place the model.

So, with GetBoundingBox you get the area that your model occupy, then, with GetPartBoundsInBox using the size gotten from GetBoundingBox you will check if the random coordinate has enough room for your model to be placed. If none parts are inside the box you place the model, if it has parts inside the box you retry a different random coordinate, until it finds room or after a certain amount of retries

image
And the relevant code:

box = place:GetBoundingBox() -- Get the part's bounding box
			repeat
				place.PrimaryPart.Position = Vector3.new(math.random(minX, maxX), place.PrimaryPart.Size.Y / 2 + v.Size.Y / 2, math.random(minZ, maxZ))
			until box:GetPartBoundsInBox() == {}

place is the model and place.PrimaryPart is the actual part.

GetBoundingBox will return 2 values, the first one the CFrame of the Model, the second one the Size that the model occupy:

local modelCFrame, modelSize = Model:GetBoundingBox()

So you were trying to use GetPartBoundsInBox() from a CFrame (box variable), and you are not providing any parameter inside, as the random CFrame for the position to check and the size you obtained from GetBoundingBox of the model.

Quick example:

local Model = workspace:WaitForChild("Model"):Clone()

local modelCFrame, modelSize = Model:GetBoundingBox()
print("This is the size that the model occupy", modelSize)

local randomCFrame = CFrame.new(0,0,0) -- GET THE RANDOM POSITION INSIDE YOUR BASE

local objectsClippingThatSpace = workspace:GetPartBoundsInBox(randomCFrame, modelSize) -- CHECKING IF ARE OBJECTS IN THAT SPACE
warn("There are:", #objectsClippingThatSpace, "occupying that space")

if #objectsClippingThatSpace > 0 then
	warn("retry with a new random CFrame inside the Base") -- until finding an empty spot
    warn(objectsClippingThatSpace)
else
	Model:PivotTo(randomCFrame) -- POSITION THE MODEL CAUSE THERE ARE NO PARTS OCCUPYING THE SPACE
end

Well I got it to work. Almost. My current code is

boxFrame, boxSize = place:GetBoundingBox() -- Get the part's bounding box
			repeat
				place.PrimaryPart.Position = Vector3.new(math.random(minX, maxX), place.PrimaryPart.Size.Y / 2 + v.Size.Y / 2, math.random(minZ, maxZ))
				task.wait()				
			until workspace:GetPartBoundsInBox(boxFrame, boxSize) == {v}

Edit: The v is from the for i, v in bases do loop so it should be the base part.
I’ve tried without the v but both give this result:


Edit 2: I’ve figured out that the problem is somehow v isn’t being seen as the part in the repeat loop but I don’t know how to fix that.

You dont need to place the Model before you check if the space is clear, thats not efficient. First check the space using a random CFrame inside the Base, if its clear its when you place the Model, not before

You dont need to provide the boxFrame to GetPartBoundsInBox, only the boxSize. Its obvious that if you place the model first, that space is already occupied by the model itself

Not true, if I remove it it tells me “Argument 2 missing or nil”

I mean, do not provide the CFrame that you got from GetBoundingBox(), the boxFrame do not use it… thats the CFrame where your original model is placed…

You need to provide a new random CFrame, the position where you want to place the model.
workspace:GetPartBoundsInBox(randomCFrame, modelSize) -- RANDOM CFRAME NOT THE boxFrame

You better check carefully the example I provided on previous replies its pretty basic and easy to understand on its steps:

local Model = workspace:WaitForChild("Model"):Clone()

local modelCFrame, modelSize = Model:GetBoundingBox()
print("This is the size that the model occupy", modelSize)

local randomCFrame = CFrame.new(0,0,0) -- GET THE RANDOM POSITION INSIDE YOUR BASE

local objectsClippingThatSpace = workspace:GetPartBoundsInBox(randomCFrame, modelSize) -- CHECKING IF ARE OBJECTS IN THAT SPACE
warn("There are:", #objectsClippingThatSpace, "occupying that space")

if #objectsClippingThatSpace > 0 then
	warn("retry with a new random CFrame inside the Base") -- until finding an empty spot
    warn(objectsClippingThatSpace)
else
	Model:PivotTo(randomCFrame) -- POSITION THE MODEL CAUSE THERE ARE NO PARTS OCCUPYING THE SPACE
end