Detecting number of players touching a part

I want a system that detects how many people are touching a part so that I can end a game round when there’s one player left (I believe it’s more professional than ‘playing’ and ‘spectating’ teams), I want it so that it won’t detect the same model player more than once, how might I do this, the following code is a hijacked roblox developer hub BasePart.Touched script

local part = script.Parent

local numTouchingParts = 0
 
local function onTouch(part)
	print("Touch started: " .. part.Parent.Name)
	numTouchingParts = numTouchingParts + 1
	print(numTouchingParts)
end
 
local function onTouchEnded(part)
	print("Touch ended: " .. part.Parent.Name)
	numTouchingParts = numTouchingParts - 1
	print(numTouchingParts)
end
 
part.Touched:Connect(onTouch)
part.TouchEnded:Connect(onTouchEnded)
8 Likes

Can you give more details on why this is how you want to detect remaining players? It’s a potentially expensive check depending on the size of the part and how many other non-player parts are touching it. And I’d be willing to bet there is a cheaper, easier criterion available.

2 Likes

it is 100 by 100 by 100 or so, all I want it to do is solely detect players touching the part nothing more or nothing less

I also want to prevent the part from detecting one player 15+ times

1 Like

You can put a .Touched handler on the 100x100x100 part (assuming it’s a cancollide=false invisible volume), call bigPart:GetTouchingParts() on it and see how many unique player’s parts are touching, by getting character model from the part and thus player name, and putting them in a dictionary each time. But if there are a lot of other things inside this box, this could be painful (it’s basically a more expensive version of FindPartsInRegion3at this point). You could also loop over the body parts of each character that you care about, and see if any of them have the box in their list of touched parts (note: still requires the box itself to have a Touched event handler, even if it’s a no-op function, if it’s not collidable). Again, probably not the best option.

But… if it happens to be box, it’s probably better to loop over the player characters themselves and do simple bounding box checks. If the box is world axis aligned, this is just min and max tests. If you can get the check down to just caring about whether or not a player’s HumanoidRootPart is inside the bounds, that’s ideal. Depends on how this zone is enclosed and what matters for gameplay.

In general, I have seen a lot of problems when a server has a Touched event on a large-volume part. People have tried this for things like VIP room kick scripts, and certain mesh parts being inside the volume, or models with lots of parts, can destroy your server’s heartbeat framerate. For a mostly-empty room, FindPartsInRegion3 will be the better option.

1 Like

To prevent it from doing logging multiple times, you can use a debounce:

local part = script.Parent
local numTouchingParts = 0 
local playersTouching = {}
part.Touched:Connect(function(hit)
     if hit.Parent:FindFirstChild("Humanoid") then --checks to make sure it is a player
          local c = hit.Parent
          if not playersTouching[c] then --if the player is not already touching the part, add them to the table and increment numTouchingParts by one
               table.insert(playersTouching, c)
               numTouchingParts = numTouchingParts + 1
          end
     end
end)

part.TouchedEnded:Connect(function(hit)
     if hit.Parent:FindFirstChild("Humanoid") then
          local c = hit.Parent
          if playersTouching[c] then -- if the player is touching the part then remove them from the table and decrement numTouchingParts by 1
               playersTouching[c] = nil
               numTouchingParts = numTouchingParts - 1
          end
     end
end)

Also like @EmilyBendsSpace said there may be a better way to do this

3 Likes

this doesn’t work, it A: doesn’t detect solely humanoids touching it and B: Doesn’t even decrease the numTouchingParts (its not that hard to do probably some mistake) im not sure, are there any possible alternatives other than detecting number of players touching a part?

1 Like

If players leave the 100x100x100 space and can’t return, you should think about just counting players as they leave. Like initialize a counter to # of players, and subtract as they leave until the counter==1. If you have any event-based means of detecting when a player has left (e.g. humanoid death), that would be better to hook into than polling with any kind of bounds checking. But if it’s only based on being in the box, consider the options from my previous post.

If character are the only thing in the 100x100x100 space, not hundreds of other parts, FindPartsInRegion3 may perform similarly to manual bounds checks. Worth considering, still better than touched events. FindPartsInRegion3WithWhiteList can be passed an array of player character Models (or the body parts you care about) as the whitelist.

1 Like

If you need to get players who are inside a specific area you can compare their positions relative to the 100x100x100 part, in a way similar to this.

local part = script.Parent

function isInsideBrick(position, brick)
	local v3 = brick.CFrame:PointToObjectSpace(position)
	return (math.abs(v3.X) <= brick.Size.X / 2)
		and (math.abs(v3.Y) <= brick.Size.Y / 2)
		and (math.abs(v3.Z) <= brick.Size.Z / 2)
end

function getPlayersInsideBox(brick)
	local players = {}
	for _, player in ipairs(game:GetService("Players"):GetPlayers()) do
		local hrp = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
		if (hrp and isInsideBrick(hrp.Position, brick)) then
			table.insert(players, player)
		end
	end
	return players
end

print(#getPlayersInsideBox(part))
16 Likes