ZonePlus utilises the new Spatial Query API and Whitelists to effectively determine players, parts and custom items within a zone. You can have as many zones as you like, as large as you like, with zero-to-minimal additional cost to performance.
For those looking to benefit from v3 you can find a detailed breakdown here.
-- 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)
-- This constructs a zone based upon a region, tracks a Zombie NPC, then listens for when the item (aka the Zombie) enters and exits the zone.
local Zone = require(game:GetService("ReplicatedStorage").Zone)
local zoneCFrame = CFrame.new()
local zoneSize = Vector3.new(100, 100, 100)
local zone = Zone.fromRegion(zoneCFrame, zoneSize)
zone.itemEntered:Connect(function(item)
print(("%s entered the zone!"):format(item.Name))
end)
zone.itemExited:Connect(function(item)
print(("%s exited the zone!"):format(item.Name))
end)
zone:trackItem(workspace.ZombieNPC)
-- This is a one-time use method which calls the given function when the given item enters the zone
local item = character:FindFirstChild("HumanoidRootPart")
zone:onItemEnter(item, function()
print("The item has entered the zone!")
end)
ZonePlus is free and open source! You’re welcome to use and modify this for any of your projects. Credit back to this thread is greatly appreciated although I won’t send the popo after you if you don’t.
I won’t be able to respond to all questions, however I pinky-promise that I’ll read your comments. We’ve put hundreds of hours into this resource so its always a joy to hear your feedback!
I was definitely pondering about subdividing a world into “zones”, where each zone would have its own environment effects and contextual information, such as a spooky forest with a boss fight.
This resource you shared suggests to me that design is not a bad idea.
I’m pretty new to scripting lua
So I need a bit of help w/ my script if u don’t mind
Basically I want to player to be given a tool once entered. And the tool destroyed once they leave the zone
local Zone = require(game:GetService("ReplicatedStorage").Zone)
local group = workspace.areanas._1.swordzone
local zone = Zone.new(group)
local Players = game:GetService("Players")
local Sword = game.ServerStorage.ClassicSword
local char = Players.Character
zone.partEntered:Connect(function(plr)
for _,v in pairs(char.Character:GetChildren()) do
if v.Name == Sword.Name then
return
end
end
for _,v in pairs(char.Backpack:GetChildren()) do
if v.Name == Sword.Name then
return
end
end
Sword:Clone().Parent = plr.Character
end)
zone.playerExited:Connect(function(plr)
if not char then return end
for _,v in pairs(char:GetChildren()) do
if v.Name == Sword.Name then
v:Destroy()
end
end
for _,v in pairs(char.Backpack:GetChildren()) do
if v.Name == Sword.Name then
v:Destroy()
end
end
end)
Character is not a valid member of Players "Players" Script 'ServerScriptService.Script', Line 7 - local char = Players.Character
I copied and pasted parts of my old zone script a friend of mine made for me so idk if it will still work
For zonePlus specifically there’s one bug I can see at a glance:
For this scenario you’ll want to use zone.playerEntered instead of partEntered.
For the entire script itself, your code breaks as you’re trying to reference Character for the PlayerService (whereas you can only do this on a player instance), try something like this instead:
local Zone = require(game:GetService("ReplicatedStorage").Zone)
local group = workspace.areanas._1.swordzone
local zone = Zone.new(group)
local swordName = "ClassicSword"
local sword = game:GetService("ServerStorage")[swordName]
zone.playerEntered:Connect(function(plr)
local char = plr.Character
local foundSword = char:FindFirstChild(swordName) or plr.Backpack:FindFirstChild(swordName)
if not foundSword then
sword:Clone().Parent = plr.Character
end
end)
zone.playerExited:Connect(function(plr)
local char = plr.Character
local foundSword = char:FindFirstChild(swordName) or plr.Backpack:FindFirstChild(swordName)
if foundSword then
foundSword:Destroy()
end
end)
Excellent Work! I am using this awesome module to make a checkpoint system. I have a question! There is any method you already made that can return which “part” player entered?
Like I have few checkpoint C1, C2, C3 etc… in the same folder.
If player entered C3 without entered C2 before. Player checkpoint should still C1. So I think I need to check which “part” player exactly entered.
There is my code I already write I think this would work too.
local checkPointGroup = game.Workspace.Map1.Zones.CheckpointZone:GetChildren()
for _, checkPoint in ipairs(checkPointGroup) do
local checkpointZone = Zone.new(checkPoint)
checkpointZone.playerEntered:Connect(function(player)
print(player.Name .. " Entered " .. checkPoint.Name)
end)
checkpointZone.playerExited:Connect(function(player)
print(player.Name .. " Exited " .. checkPoint.Name)
end)
end
But just wondering is there is a method or other way can return which “part” player entered? Thank you for this awesome module!
When the player enters, you could iterate through it’s character parts and call zone:findPart(basePart) to determine which of its parts are within the zone.
Alternatively you could use the partEntered and partExited events when BasePart.CanTouch goes live.
No clue why but Zone+ V2 doesnt even work for me, I’ve tried to do a safezone, tampered with it, then went to your original script and used it but it still didn’t work. It could be because I put the spawn into the safezone?
v2 isn’t backwards compatible with v1 (due to the new optimisations which require a different API) therefore if you’re using code from v1 this is likely why.
It’s also important to note that the additionalHeight parameter has been removed, therefore the group-parts that make up the zone have to encapsulate the entire area of the zone.
You can view coded examples for v2 at the new playground, including a safe zone example, and for more information you can visit the docs.
I have V2 but I didn’t remember to remove additionalHeight, thank you so much for getting a quick response. Honestly, I enjoy zone+ V2, good job making it.
Whenever I click play, both in Studio and in real servers, the descendant BaseParts of my ZoneGroup get destroyed. Do you know of a solution to this issue?
There are no other scripts in the game at all, and it’s currently just meshes and baseparts in the game.
My script is as follows:
local Zone = require(game:GetService("ReplicatedStorage").Zone)
local zoneGroup = workspace.LobbyZoneGroup
local zone = Zone.new(zoneGroup)
zone.localPlayerEntered:Connect(function()
PlayThing()
end)
ZonePlus shouldn’t be destroying any descendant baseparts. Can you provide a video showing the workspace before and after, or DM me a stripped-down place file with the zone where parts are being destroyed.