Parts with math.random positions overlapping positions

  1. What do you want to achieve? Keep it simple and clear!
    I’m spawning multiple monster blocks in a smallish field and I want the objects to have enough space to not touch each other.

  2. What is the issue? Include screenshots / videos if possible!
    The Instances are overlapping and causing issues with their CFrames. Bumping and jumping and pushing each other in order to occupy the same position.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I’ve tried destroying the parts if they touch each other, but I’m not sure if my definitions are up to par.

I’ve also tried changing math.random to ((math.random)*10) in order to spawn items at farther intervals but of course they still occasionally overlap.
I’ve tried several ways to do this including multiple different attempts at definitions, several of them run without any errors but do not destroy any rocks.

My Rock is stored in rep storage and is parented to folder in game.Workspace.Rocks

local function onTouch(otherPart)
	print (otherPart.Parent)
	if otherPart.Parent == Rock
		then rock:Destroy()
	
	else
	

and

local rock = script.Parent
local label = rock.Health
local hit_sound = rock.Hit
local break_sound = rock.Break
local debounce = false
local hp = rock.Stats.HP.Value
local maxHP = rock.Stats.MaxHP.Value
local swings = rock.Stats.Swings.Value
local rockPos = rock.Position
local Rocks = game.Workspace.Rocks

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

local function Round(n, decimals) --Decimal rounding for rounding total swing calculation fractions down
	decimals = decimals or 0
	return math.floor(n * 10^decimals) / 10^decimals
end


local function onTouch(otherPart) --THIS IS WHERE I TRY TO DESTROY OVERLAP!
	print (otherPart.Parent)
	if otherPart.Parent.Parent == game.Workspace.Rocks
		then rock:Destroy()
	
	else
	
	local tool = otherPart.Parent
	
	if tool:IsA('Tool') and tool.Mining.Value == true and debounce == false and tool.Name == "Hammer" then
		debounce = true
		local hp = rock.Stats.HP.Value
		
		swings = swings +1
		rock.Stats.Swings.Value = rock.Stats.Swings.Value +1 
		
		
		wait()
		print ("tool is "..tool.Name)
		local player = tool.parent
		print ("player is "..player.Name)
		print ("maxHP is "..maxHP)
		local power = tool.Power.Value
		print ("power is "..power)
		
		--total swings needs WHOLE NUMBER VALUE
		local calcSwings = (maxHP/power)
		
local totalSwings=((Round(calcSwings, 0)+1))
		print ("totalSwings is "..totalSwings)
		
		rock.Stats.HP.Value = rock.Stats.HP.Value - power
		print ("stored HP value is "..rock.Stats.HP.Value)
		
		local currentHP = maxHP - ((swings) * power)
		print ("calculated HP is "..currentHP)
		
		
		
		
		
		print ("swings at "..swings)

		rock.Stats.swingsLeft.Value = totalSwings - swings
		hit_sound:Play()
		
		--total swings needs whole number value for GUI
		
		local swingsLeft = rock.Stats.swingsLeft.Value
		
		
		label.Green.Size = UDim2.new(swingsLeft/totalSwings, 0, 1, 0)
	end

		if rock.Stats.HP.Value <= 0 then
		break_sound:Play()
		wait(.5)
		rock:Destroy()
		
		local crystals= ReplicatedStorage:FindFirstChild('Crystals'):GetChildren()
		
		print(crystals)
		
		
		local randomCrystal = crystals[math.random(1,#crystals)]:Clone()
		
	
		
		
		randomCrystal.Parent = game.Workspace
		randomCrystal.Position = rockPos+Vector3.new(0,-3,0)
		
		local collectScript = ServerStorage:FindFirstChild("Collect"):Clone()
		collectScript.Parent = randomCrystal
		end
wait(1)debounce = false
end
end

rock.Touched:Connect(onTouch)

Sorry my code is so ugly, I’m still really new. There’s a lot of old debugging prints in there. I hope it doesn’t make it too difficult to read. I’m hoping there’s a method that doesn’t require me to write a new method of finding random positions. If you need that code it is,

local rock = game.ReplicatedStorage.Rock
local count = Instance.new("IntValue")
--local anchorPOS = rock.Anchor.Position

local function makeRock()
	local clone = rock:Clone()
	local clonePOS = Vector3.new(math.random(-50,50), 4, math.random(-50,50))
	clone.Anchor.Position = clonePOS
	clone.Position = clonePOS
	
	clone.Parent = workspace.Rocks
	count.Value += 1
	local connection
	connection = clone:GetPropertyChangedSignal("Parent"):Connect(function()
		connection:Disconnect()
		count.Value -= 1
		if count.Value < 50 then
			makeRock()
		end
	end)
end


for i = 1, 50 do
	makeRock()
	wait()
end
1 Like

you may want to compare the clonePOS with the other positions of the other ‘monsters’, and call the random function again if it is within a certain distance from another one. This can be quite inefficient if you have a lot of them in a small space but it could work for your application.

1 Like

Thanks that’s definitely something else I can check out. I’m so new at scripting that I’m just excited to make things work and don’t particularly mind inefficiencies yet. The instances should only be a dozen or so, I just wanted the issue fixed before I start designing an area to actually put them.

I also tried spawning them in predetermined positions but my client wasn’t updating after they initially spawned.

Not tested yet, but I’ve come up with something that might work. Keep in mind that after it tries 10 times, it will just set the position to the previous position (which is within the exclusion radius) but hopefully this works for the most part for you. Feel free to change the radius of exclusion (marked with comments) to define how close two rocks can be to each other before it re-calculates a new random position. You can also change the maxitteration variable to something higher or lower depending on your results.

local rock = game.ReplicatedStorage.Rock
local count = Instance.new("IntValue")
--local anchorPOS = rock.Anchor.Position

local rockPositions = {}

local function makeRock()
	local clone = rock:Clone()
	local found = true
	local maxitteration = 10 --a 'timeout' to stop calculating after this amount so that you're computer doesn't die
	local clonePOS = Vector3.new(math.random(-50,50), 4, math.random(-50,50))

	for i=0, maxitteration do
		for _,v in ipairs(rockPositions) do
			if (v - clonePOS).Magnitude <= 5 then --radius of exclusion
				found = false
				break --too close, break the loop and re-generate a random point
			end
		end
		if found then
			break
		else
			clonePOS = Vector3.new(math.random(-50,50), 4, math.random(-50,50))
		end
	end
	table.insert(rockPositions,clonePOS)

	clone.Anchor.Position = clonePOS
	clone.Position = clonePOS
	
	clone.Parent = workspace.Rocks
	count.Value += 1
	local connection
	connection = clone:GetPropertyChangedSignal("Parent"):Connect(function()
		connection:Disconnect()
		count.Value -= 1
		if count.Value < 50 then
			makeRock()
		end
	end)
end


for i = 1, 50 do
	makeRock()
	wait()
end
1 Like

Thank you! It doesn’t seem to work when I swap it into my MakeRocks script, but I see what you were aiming at. Hopefully I can get it to work after dinner, it looks like a really cool idea to make sure the instances are spaced out!

I may have misplaced a variable declaration. Try this:

local rock = game.ReplicatedStorage.Rock
local count = Instance.new("IntValue")
--local anchorPOS = rock.Anchor.Position

local rockPositions = {}

local function makeRock()
	local clone = rock:Clone()
	local maxitteration = 10 --a 'timeout' to stop calculating after this amount so that you're computer doesn't die
	local clonePOS = Vector3.new(math.random(-50,50), 4, math.random(-50,50))

	for i=0, maxitteration do
		local found = true
		for _,v in ipairs(rockPositions) do
			if (v - clonePOS).Magnitude <= 5 then --radius of exclusion
				found = false
				break --too close, break the loop and re-generate a random point
			end
		end
		if found then
			break
		else
			clonePOS = Vector3.new(math.random(-50,50), 4, math.random(-50,50))
		end
	end
	table.insert(rockPositions,clonePOS)

	clone.Anchor.Position = clonePOS
	clone.Position = clonePOS
	
	clone.Parent = workspace.Rocks
	count.Value += 1
	local connection
	connection = clone:GetPropertyChangedSignal("Parent"):Connect(function()
		connection:Disconnect()
		count.Value -= 1
		if count.Value < 50 then
			makeRock()
		end
	end)
end


for i = 1, 50 do
	makeRock()
	wait()
end

After you have had something to eat let me know if this works, and if not check if there are any errors. I’ll hop on studio and try to apply this.

2 Likes

Wow, that is absolutely amazing! I haven’t learned maxiteration or magnitude of vectors yet. I’ll put the terms on my to do list! I drastically increased the concentration to make sure they don’t overlap and your script did it with flying colors! Thank you!!!

1 Like