Efficient Way To Check When Player Leaves a Part

Hello everyone,
I am making a script so that a UI pops up when a player is on the shopEnter part, and closes when the player leaves the part.
However, the only efficient way to do this that I’m aware of is getting .TouchEnded and getting the character that way, but .TouchEnded is called even when the player doesn’t touch the part and just walks or an animation plays etc

Can you clarify this part a little bit more? It’s kind of hard to understand.

Do you mean getting the character when TouchEnded() is called? Why not just directly close the GUI when the player leaves the part? Unless I’m missing something that requires the character in the TouchEnded() function.

1 Like

Sorry, I mean Part.TouchEnded(), and then get the char from that

The problem is with how .Touched works: if you stop moving while on the part, there’s no “still on the part” event. They end up firing every time the player moves while still on the part.
I can give you a zone code that can handle this perfectly. Would you like that posted?

1 Like

Yes please, that would be very helpful!

But can’t you also do TouchEnded on the zone part instead of checking every frame?

Touch will act oddly with how you’re using it here. Many run into this problem with .TouchEnded()…
Let me re-do this with one script, cutting out the generic set-up part and just focusing on a manually set up Region3.

--ServerScript in ServerScriptService

local rns = game:GetService("RunService")
local zoneRegion = Region3.new(Vector3.new(-10, 0, -10), Vector3.new(10, 10, 10)) -- Set your own region coordinates

local trackedItems = {}
local itemEntered = Instance.new("BindableEvent")
local itemExited = Instance.new("BindableEvent")

itemEntered.Event:Connect(function(item)
	print(("%s entered the zone!"):format(item.Name))
end)

itemExited.Event:Connect(function(item)
	print(("%s exited the zone!"):format(item.Name))
end)

task.spawn(function()
	while true do
		local itemsInZone = {}
		local parts = workspace:FindPartsInRegion3(zoneRegion, nil, math.huge)

		for _, part in ipairs(parts) do
			local item = part.Parent
			if item:FindFirstChild("Humanoid") then
				if not trackedItems[item] then
					trackedItems[item] = true
					itemEntered:Fire(item)
				end
				itemsInZone[item] = true
			end
		end

		for item in pairs(trackedItems) do
			if not itemsInZone[item] then
				trackedItems[item] = nil
				itemExited:Fire(item)
			end
		end

		rns.Stepped:Wait()
	end
end)

Keep in mind this is a box (Region3) set up over (on top of) the part you’re stepping on.

Utility script to get coordinates if needed.

part to Region3 call
--using a (box) part named Zone as a reference
local part = workspace:WaitForChild("Zone")

local size = part.Size
local position = part.Position

local min = position - size / 2
local max = position + size / 2

print(("local zoneRegion = Region3.new(Vector3.new(%.0f, %.0f, %.0f), Vector3.new(%.0f, %.0f, %.0f))")
	:format(min.X, min.Y, min.Z, max.X, max.Y, max.Z))

--copy from the output
2 Likes

You might want to check out this resource by ForeverHD:

Here is some example code from that:

-- This constructs a zone based upon a group of parts in Workspace and listens for when a player enters and exits this group
-- There are also the ``zone.localPlayerEntered`` and ``zone.localPlayerExited`` events for when you wish to listen to only the local player on the client
local Zone = require(game:GetService("ReplicatedStorage").Zone)
local container = workspace.AModelOfPartsRepresentingTheZone
local zone = Zone.new(container)

zone.playerEntered:Connect(function(player)
    print(("%s entered the zone!"):format(player.Name))
end)

zone.playerExited:Connect(function(player)
    print(("%s exited the zone!"):format(player.Name))
end)
2 Likes

I’m going to implement this script and I’ll tell you if it works well

Region3 methods are deprecated. It’s best to use workspace:GetPartsInPart() and/or workspace:GetPartBoundsInBox() instead. I also agree with BendsSpace.

2 Likes

Then why not post your scripts? Let your programming do the talking…

GetPartsInPart()
--this version will need the zonepart to stay on the workspace.
local zonePart = workspace:WaitForChild("ZonePart")

zonePart.Transparency = 1
--these can be set on the part itself
zonePart.CastShadow = false
zonePart.CanCollide = false
zonePart.CanTouch = true
zonePart.CanQuery = true
zonePart.Anchored = true
zonePart.Locked = true
--

local rns = game:GetService("RunService")

local trackedItems = {}
local itemEntered = Instance.new("BindableEvent")
local itemExited = Instance.new("BindableEvent")

itemEntered.Event:Connect(function(item)
	print(("%s entered the zone!"):format(item.Name))
end)

itemExited.Event:Connect(function(item)
	print(("%s exited the zone!"):format(item.Name))
end)

task.spawn(function()
	while true do
		local itemsInZone = {}
		local touching = workspace:GetPartsInPart(zonePart)

		for _, part in ipairs(touching) do
			local item = part:FindFirstAncestorOfClass("Model")
			if item and item:FindFirstChild("Humanoid") then
				if not trackedItems[item] then
					trackedItems[item] = true
					itemEntered:Fire(item)
				end
				itemsInZone[item] = true
			end
		end

		for item in pairs(trackedItems) do
			if not itemsInZone[item] then
				trackedItems[item] = nil
				itemExited:Fire(item)
			end
		end

		rns.Stepped:Wait()
	end
end)

GetPartBoundsInBox()
--this version will need the zonepart to stay on the workspace.
local zonePart = workspace:WaitForChild("ZonePart")

zonePart.Transparency = 1
--these can be set on the part itself
zonePart.CastShadow = false
zonePart.CanCollide = false
zonePart.CanTouch = true
zonePart.CanQuery = true
zonePart.Anchored = true
zonePart.Locked = true
--

local rns = game:GetService("RunService")

local trackedItems = {}
local itemEntered = Instance.new("BindableEvent")
local itemExited = Instance.new("BindableEvent")

itemEntered.Event:Connect(function(item)
	print(item.Name .. " entered the zone!")
end)

itemExited.Event:Connect(function(item)
	print(item.Name .. " exited the zone!")
end)

task.spawn(function()
	while true do
		local cf = zonePart.CFrame
		local size = zonePart.Size
		local parts = workspace:GetPartBoundsInBox(cf, size)
		local itemsInZone = {}

		for _, part in ipairs(parts) do
			local item = part:FindFirstAncestorOfClass("Model")
			if item and item:FindFirstChild("Humanoid") then
				if not trackedItems[item] then
					trackedItems[item] = true
					itemEntered:Fire(item)
				end
				itemsInZone[item] = true
			end
		end

		for item in pairs(trackedItems) do
			if not itemsInZone[item] then
				trackedItems[item] = nil
				itemExited:Fire(item)
			end
		end

		rns.Stepped:Wait()
	end
end)

This is more of a technique to do this along with a working command.
The outcome here you should be going for is… posting a working script/technique.
I’m looking for a way to fix the issue here, and testing techniques is how you get there.
I’m not looking for a debate. If you have something that will work, post it.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.