Check For Players Entered and Left Parts

So I have a folder in my workspace, and in the folder there are parts that I want to check when players entered them, and check when they left.

that’s how I did it:

local Players = game:GetService("Players")
local PartsToCheck = workspace.Parts:GetChildren()

local function CheckIfFoundPlayer(partToCheck)
	local plrsInside = {}
	local oldPlayers = {}
	local parts, plr
	while wait() do
		-- get parts inside part
		parts = workspace:GetPartsInPart(partToCheck)
		
		-- check if parts are parts of players
		for i, v in pairs(parts) do
			if v.Parent:FindFirstChild("Humanoid") then
				plr = Players:GetPlayerFromCharacter(v.Parent)
				if not table.find(plrsInside, plr) then
					if not table.find(oldPlayers, plr) then -- not found in old list, means it's new player that entered
						print(plr.Name .. " Has Entered")
					end
					table.insert(plrsInside, plr)
				end
			end
		end
		
		-- check if players left
		for i, v in pairs(oldPlayers) do
			if not table.find(plrsInside, v) then
				print(v.Name .. " Has left")
				table.remove(oldPlayers, i)
			end
		end
		
		-- add new players to the old table
		for i, v in pairs(plrsInside) do
			if not table.find(oldPlayers, v) then
				table.insert(oldPlayers, v)
			end
		end
		table.clear(plrsInside) -- clean to start finding new
	end
end

for i, v in pairs(PartsToCheck) do
	coroutine.wrap(CheckIfFoundPlayer)(v)
end

Question is, will that make any performance issues if there are a lot parts in the folder? is there a way to make it better? (btw touched event is really buggy, that’s why i don’t use that.)

1 Like

why not just use region3 where this functionality is supported

wdym? region3 is basically doing the same thing I made but with more steps. you have to create a region3 variable first, setting it’s vectors and only then calling Workspace:FindPartsInRegion3(region3). And that’s instead just calling workspace:GetPartsInPart(partToCheck) which is what I did.

Yeah it’s possible if you had a lot of parts within that part, there could be some performance issues.


Check these two posts regarding an alternative that just checks each of the players. (and is probably more performant) :slight_smile:

1 Like

I changed it a bit, does that look better?

local Players = game:GetService("Players")
local PartsToCheck = workspace.Parts:GetChildren()

local 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

local function GetAllPlayersInsidePart(playersAlreadyInside, part)
	local list = {}
	for _, player in ipairs(Players:GetPlayers()) do
		if player.Character then
			local torso = player.Character:FindFirstChild("HumanoidRootPart")
			if torso and isInsideBrick(torso.Position, part) then
				table.insert(list, player)
			end
		end
	end
	return list
end

local function CheckNewPlayersInside(oldTable, newTable)
	for i, v in pairs(newTable) do
		if not table.find(oldTable, v) then
			print(v.Name .. " Entered")
		end
	end
end

local function CheckPlayersLeft(oldTable, newTable)
	for i, v in pairs(oldTable) do
		if not table.find(newTable, v) then
			print(v.Name .. " Has left")
		end
	end
end

local function CheckIfFoundPlayer(partToCheck)
	local plrsInside = {}
	local oldPlayers = {}
	local parts, plr
	while wait() do
		-- get players inside
		plrsInside = GetAllPlayersInsidePart(oldPlayers, partToCheck)
		
		-- check players entered
		CheckNewPlayersInside(oldPlayers, plrsInside)
		
		-- check players left
		CheckPlayersLeft(oldPlayers, plrsInside)
		
		-- replace old with new
		oldPlayers = plrsInside
	end
end

for i, v in pairs(PartsToCheck) do
	coroutine.wrap(CheckIfFoundPlayer)(v)
end

also do I have to worry about creating a new coroutine for each part?

1 Like

Region3 is pretty much inferior to GetPartsInPart.

1 Like