I’m trying to set a Zone system, similar to Fisch where it checks the location of the bobber, in my case the player relative to invisible parts in the workspace. The only problem is that when I check if the player has touched a new zone, it takes a good 5 seconds to register. I saw there used to be APIs for this like ZonePlus, but every one I’ve found seems quite unnecessarily complicated and Outdated. What is the best way to register players to the zone?
I’d like to add that I don’t need a constant check, just something that registers the player when joining a new zone.
The best way is to use Spatial Queries. If you don’t want to use .Touched, then you have to have a constant check and use them. But the performance hit is almost NOTHING.
--!native
--!optimize 2
-- Local script is best here, this script should basically cost NOTHING in performance
local Players = game:GetService("Players")
local Player = Players.LocalPlayer
local ZoneFolder = workspace:WaitForChild("insert here") -- assuming you have this
local Params = OverlapParams.new()
Params.IncludeInstances = {ZoneFolder}
Params.MaxParts = 1 -- we only care if we are in ONE zone.
-- no need to search all parts
local CurrentZone: BasePart? = nil
local function CheckZone()
local character = Player.Character
if not character then
return
end
local hrp = character:FindFirstChild("HumanoidRootPart") :: BasePart
if not hrp then
return
end
local hrpCFrame: CFrame = hrp.CFrame
local hrpSize: vector = hrp.Size
local intersectingZones = workspace:GetPartBoundsInBox(hrpCFrame, hrpSize, Params)
local foundZone = intersectingZones[1]
-- we dont wanna fire twice for the same zone
if foundZone ~= CurrentZone then
CurrentZone = foundZone
-- if we leave then getparts returns empty table, foundZone is nil
if CurrentZone then
print("new zone")
else
print("left all zones")
end
end
end
task.spawn(function()
while task.wait(0.1) do
CheckZone()
end
end)
Although, GetPartsBoundInBox is usually used for easy shapes, if you have some complex ones then GetPartsInPart might be better. And, if you have overlapping zones then the MaxParts should probably not be set..
Drop this in a part on the workspace for a quick test.
--ServerScript
local rs=game:GetService("RunService")
local part = script.Parent
local prev = {}
function getHumanoids()
local humans = {}
for _, p in pairs(workspace:GetPartsInPart(part)) do
local h = p.Parent:FindFirstChildWhichIsA("Humanoid")
if h and not table.find(humans, h) then table.insert(humans, h) end
end rs.Heartbeat:Wait()
return humans
end
while(true)do rs.Stepped:Wait()
local now = getHumanoids()
for _, h in pairs(now) do
if not table.find(prev, h) then
print(h.Parent.Name.." entered")
end
end
for _, h in pairs(prev) do
if not table.find(now, h) then
print(h.Parent.Name.." exited")
end
end prev = now
--task.wait(0.033)
end
I’ve made a few variations of this, but this is the basic concept done on one part.
It can even do a cylinder. I’ve never tried a ball. It’s more of a workaround for TouchEnded.
Their is a module for more reliable zone handling… I don’t quite remember the name, but I think it was called zone plus… if that’s not the name you can probably find it on YouTube
There is nothing more reliable than my script for zones (in my opinon).. Not even close to my first try.
That is a version for only one zone however.. Very easily modified to be as many as you want.