What is an efficient way to detect if a player has left a Region3?

Hello! I am making an alarm system where if you are on the right team and have a tool either equipped or inside your backpack, the script will fire a BindableEvent to another script which will activate/deactivate the alarm. I have gotten the alarm to fire when you enter the Region3, but I am confused on how to detect if they left the region. Someone on Discord told me to use magnitude or rays, but I am not good at math so I don’t know magnitude a lot, and I am confused on how to implement rays. I tried detecting how they leave the region the same way how I detect if they entered, but the issue is that once they enter, the alarm activates for only a second, deactivates, then never activates again.

local remote = game.ServerStorage:WaitForChild("Alarms")
local disable = game.ServerStorage:WaitForChild("Disable")

function PartToRegion3(obj)
	local abs = math.abs
	
	local cf = obj.CFrame
	local size = obj.Size
	local sx, sy, sz = size.X, size.Y, size.Z 
	
	local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = cf:components()
	
	local wsx = 0.5 * (abs(R00) * sx + abs(R01) * sy + abs(R02) * sz)
	local wsy = 0.5 * (abs(R10) * sx + abs(R11) * sy + abs(R12) * sz)
	local wsz = 0.5 * (abs(R20) * sx + abs(R21) * sy + abs(R22) * sz) 
	
	local minx = x - wsx
	local miny = y - wsy
	local minz = z - wsz
	
	local maxx = x + wsx
	local maxy = y + wsy
	local maxz = z + wsz
	
	local minv, maxv = Vector3.new(minx, miny, minz), Vector3.new(maxx, maxy, maxz)
	return Region3.new(minv, maxv)
end

local region = PartToRegion3(script.Parent)
local playerIn = script:WaitForChild("PlayerInRegion")
local remote = game.ServerStorage:WaitForChild("Alarms")
local disable = game.ServerStorage:WaitForChild("Disable")
local alreadyFired = false
local alreadyDisabled = false

local function insideRegion()
	while not playerIn.Value do
		wait(.5)
		local parts = workspace:FindPartsInRegion3(region, script.Parent)
		
		for _,part in ipairs(parts) do
			if game.Players:GetPlayerFromCharacter(part.Parent) then
				local player = game.Players:GetPlayerFromCharacter(part.Parent)
				local char = part.Parent
				if player.TeamColor == BrickColor.new("Bright orange") and not alreadyFired then
					
					for i,v in ipairs(player.Backpack:GetChildren()) do
						if v:IsA("Tool") then
							if v.IsContraband.Value then
								remote:Fire(player, script.Parent)
								alreadyFired = true
								break
							end
						end
					end
					
					local tool = char:FindFirstChildOfClass("Tool")
					
					if tool and not alreadyFired then
						remote:Fire(player, script.Parent)
					end
					playerIn.Value = true
					break
				end
			end
		end
	end
end

local function outsideRegion()
	while playerIn.Value do
		wait(.5)
		local parts = workspace:FindPartsInRegion3(region, script.Parent)
		
		for _,part in ipairs(parts) do
			local player = game.Players:GetPlayerFromCharacter(part.Parent)
			if player and not alreadyDisabled then
				if player.TeamColor == BrickColor.new("Bright orange") then
					
					for i,v in ipairs(player.Backpack:GetChildren()) do
						if v:IsA("Tool") then
							if v.IsContraband.Value then
								disable:Fire(player, script.Parent)
								alreadyDisabled = true
								break
							end
						end
					end
					
					local tool = player.Character:FindFirstChildOfClass("Tool")
					
					if tool and not alreadyDisabled then
						disable:Fire(player, script.Parent)
					end
					wait(1)
					alreadyDisabled = false
					playerIn.Value = false
					break
				end
			end
		end
	end
end

playerIn.Changed:Connect(function()
	insideRegion()
end)

playerIn.Changed:Connect(function()
	outsideRegion()
end)

The function PartToRegion3 I found on a post on the DevForum, credit goes to them, I forgot who.

1 Like