How to tell if 2 parts are on the same spot?

Currently working on my placement system, and each model has a PrimaryPart (a Hitbox)

Point of the Hitbox is for when placing furniture, so furniture can’t intersect. I have a separate system checking for collision of hitboxes for furniture, however, I am working on walls. Now walls also have a Hitbox, as that Hitbox is what’s used for the movement and players can intersect walls, however, you can currently spam click on the same spot and place the same wall on top of each other. The chances of this becoming a massive issue in my game is unlikely (as it costs money to place walls down, so no point in placing 100 walls in the same spot) but I still want to stop this from happening in case somebody accidentally double clicks.

So in short, is there a way to check for models PrimaryPart having the same position as an already placed item? I know I could probs do something like

if placingModel.PrimaryPart.Position == model.PrimaryPart.Position then print('Can't place') end

put if there are hundreds of models.

Tried using something like this:

click = mouse.Button1Down:connect(function()
		for _, allModels in pairs(playersPlot.Purchases:GetChildren()) do
			if wallClone.PrimaryPart.Position ~= allModels.PrimaryPart.Position then
				click:Disconnect()
				renderStepped:Disconnect()
				place:FireServer(wallClone.Name, wallClone.PrimaryPart.CFrame)
				wallClone:Destroy()
				self:StartPlacing(playersPlot, mouse, wallType)
			end
		end
	end)

But that ended up firing the place event 30-40 times :sweat_smile:

1 Like

Comparing vectors like this probably doesn’t work all the time because of floating point errors. If you are checking whether two positions are really close / the same as each other, check if the distance between the two is below a certain small value, like 0.0001:

local sigma = 0.0001
(...)
if (wallClone.PrimaryPart.Position - allModels.PrimaryPart.Position).magnitude > sigma then

Other than that, I think you can use GetTouchingParts here on the hitbox to pre-filter workspace on just the parts that are touching that hitbox, then you can only use that list rather than looping over the entire model list.

4 Likes

Don’t think GetTouchingParts would work, as the part you are placing has CanCollide set to false. This is so your character doesnt get in the way of the part being placed

See: Simple trick to make GetTouchingParts work with non-CanCollide parts

You can attach an empty touched handler while the part is being moved around and then disconnect it when placed.

But touching parts would still be fired if the are touching, but not in the exact same position? I need it to check if the parts are in the exact same position, orientation, CFrame, whatever

You don’t care about the touched connection, you just attach an empty touched handler so that GetTouchingParts will work (see the linked thread).

Yeah I checked the link, but this would fire if the parts are touching. I’m not after if the parts are touching, I need if they are in the exact same spot.

By the way, this happens because you are not breaking out of the loop after firing “place” once. Do this inside of the if-statement

(...)
	click:Disconnect()
	renderStepped:Disconnect()
	place:FireServer(wallClone.Name, wallClone.PrimaryPart.CFrame)
	wallClone:Destroy()
	self:StartPlacing(playersPlot, mouse, wallType)
	break
(...)

I understand that. Please read my earlier post. It allows you to get a filtered list of things to check for, so that you don’t need to loop over every model you have in workspace / wherever and compare it to each one, but rather only a subset that is already touching the part (then you do your extra checks on top of that). You wouldn’t just use the result of GetTouchingParts raw.

for _, allModels in pairs(playersPlot.Purchases:GetChildren()) do
		if (wallClone.PrimaryPart.Position - allModels.PrimaryPart.Position).magnitude > 0.0001 then
			click:Disconnect()
			renderStepped:Disconnect()
			place:FireServer(wallClone.Name, wallClone.PrimaryPart.CFrame)
			wallClone:Destroy()
			self:StartPlacing(playersPlot, mouse, wallType)
			break
		end
	end

The break stops the placing of multiple models at once, but you can still place parts on top of each other

Right, I see what you are trying to do now. You’ll want to do all your checks first and jump out of the function once you find something that blocks the place. So the code would be:

click = mouse.Button1Down:connect(function()
	for _, allModels in pairs(playersPlot.Purchases:GetChildren()) do
		if (wallClone.PrimaryPart.Position - allModels.PrimaryPart.Position).magnitude < 0.0001 then
			return -- blocked
		end
	end
	-- not blocked
end)
1 Like

Rip, I really thought this would have worked, but now it doesn’t place at all

	local click
	click = mouse.Button1Down:connect(function()
		print('Click')
		for _, allModels in pairs(playersPlot.Purchases:GetChildren()) do
			if (wallClone.PrimaryPart.Position - allModels.PrimaryPart.Position).magnitude > 0.0001 then
				print('Model found with same position!')
				return
			end
		end
		print('Place model')
		click:Disconnect()
		renderStepped:Disconnect()
		place:FireServer(wallClone.Name, wallClone.PrimaryPart.CFrame)
		wallClone:Destroy()
		self:StartPlacing(playersPlot, mouse, wallType)
	end)

Prints Click and Model found with same position! even with no models even close to the placing model

You gotta flip the condition on the if-statement, so < .0001 instead. Currently you have > .0001 will go through for the first part that isn’t in the position, you want it to fall through when the part is in the position instead.

What you could do is GetTouchingParts() or use an region3 with the hitbox size.

Omg :man_facepalming:

The > 0.0001 wasn’t visible in my code (I have small screen + large explorer and properties tabs so didn’t even think of it :man_facepalming:) thank you!! :sweat_smile:

2 Likes