ZonePlus v3.2.0 | Construct dynamic zones and effectively determine players and parts within their boundaries

Need to be using LocalPlayerEntered
snippet from my work:

local zone = Zone.new(group)
	zone:bindToGroup('SectionZones')
	zone:setDetection("Centre")
	zone.localPlayerExited:Connect(function()
		activeZones = activeZones-1
		print(activeZones)
		if activeZones == 0 then
			changeLighting(Lighting.Outside)
		end
	end)
	zone.localPlayerEntered:Connect(function()
		activeZones = activeZones+1
		if Lighting[group.Name] then
			changeLighting( Lighting[group.Name] )
		end
	end)
3 Likes

Ah thanks. I didn’t realize there was a different event for local scripts. Lol

1 Like

This is so good Roblox should hire you to include it natively into Roblox Studio, very usefull, most likely every game made in Roblox soon or later will need some kind of zone checking making devs most of the times use poorly made checking without proper performance. With your module developers get it for free, with good performance and very easy to use. Congrats on your great work!

1 Like

Did you ever find out what the issue was? Looked at the github and dont see any updates.

This is really awesome. One thing though, I’m creating like a spatial audio type of thing as presented in one of the examples. The problem is that I have some parts inside of the zone that contains SurfaceGUIs with buttons on them. The button events won’t be registered as the zone is in the way.

Trying CanQuery actually works, but then completely disables the zone.
Any recommendations on how to solve this problem?

I’m just waiting back on this bug report then plan to continue exploring your issue:

1 Like

zone:relocate() should do the trick!

2 Likes

I’m having problems with using the :getParts() function. For some reason, it still returns parts that were inside the region, but are not anymore, until those parts move again.

Essentially, if a player enters using partEntered and then leaves using partExited, the :getParts() method still returns the parts of the player even though they are nowhere near the zone. partEntered and partExited still work as intended however, which is odd. If i return the part provided by partExited, it very clearly provides parts of the player that aren’t in the zone anymore. getParts(), on the other hand, says thay are still inside.

In this example, the baseplate turns green when parts are inside the zone, and red when there are none. I walked into the zone (transparent pink part) and it turned green but walking out did not turn red:


I’m printing the results of getParts() and the bodyparts are still being returned when they shouldn’t:
image

As soon as i then move, it realises the parts aren’t in the zone anymore:

Please excuse my bad code here, I was trying to see if it was just partEntered being fired after the last part left or something by constantly checking for parts until there are none anymore:

Essentially, when a part enters the zone, it starts a runservice loop that checks for parts using getParts() until the number of parts returned is 0, where the connection is terminated and the zone waits for a new part before repeating.
I know this is a terrible solution but i was just trying to work out if it was my implementation or if it was the zone module having issues. It appears to be the latter.

(SharedFunctions:IsTagged() is just a checker for parts that are tagged. All parts of the player are tagged correctly so this isn’t causing the problem. Same results happen with that omitted)

Any help appreciated!

Edit: The Zone is just being made with that transparent pink part, a normal brick, from this line:

Are you able to perform these checks using the item events and zone:trackItem() instead of the part events? If so, does that work? Item events are also greatly more optimal than the part events.

Can you link me to an uncopylocked place which reproduces this issue?

2 Likes

I could possibly use trackitem instead, i can track more than one at once, right?
I stripped the project of all code that wasnt causing the issue. The issue happens inside the Floorbutton modulescript, found inside the TestButton model in workspace. The code is identical to the one in the post above.
lingering getparts.rbxl (73.1 KB)

Edit: How performant would trackitem be if i have say 10 buttons with 10 items? each button would need to track each item itself PLUS the player? There will only be one player per server.
And if an item being tracked is removed, is it automatically removed from the track list or do I have to do that manually?

Yep you’re right, thanks for sharing that. The partEntered and partExited events are working correctly which suggests a slight logic error in the getParts method when used independently.

I’ll open up an issue, and for the time being have a look at updating your code to something similar to the following:

-- Events
local totalPartsInZone = 0

self.Update.Event:Connect(function()
	local anyPartsInZone = totalPartsInZone > 0
	workspace.Baseplate.BrickColor	= (anyPartsInZone and BrickColor.Green() or BrickColor.Red())
end)

self.Zone.partEntered:Connect(function(part)
	totalPartsInZone += 1
	self.Update:Fire()
end)

self.Zone.partExited:Connect(function(part)
	totalPartsInZone -= 1
	self.Update:Fire()
end)
2 Likes

Oh my god i cant believe i didn’t think of that as a solution, that’s so easy! I’ll try that now and see how it goes.
Thanks for your help and hope it gets solved soon!

1 Like

Because of how well optimised spatial queries are and the techniques ZonePlus uses internally, tracking additional parts of typical volumes adds negligible amounts to performance.

You can try this yourself in studio:

20 parts being tracked

~0.4% of activity

local Zone = require(script.Parent.Zone)
local zone = Zone.new(script.Parent.Hitbox)

for i = 1, 20 do
	local part = Instance.new("Part")
	part.Name = "Part"..i
	part.Anchored = true
	part.Parent = workspace
	zone:trackItem(part)
	print("track item!")
end

zone.itemEntered:Connect(function(item)
	print("entered: ", item.Name)
end)

zone.itemExited:Connect(function(item)
	print("exited", item.Name)
end)


10,000 parts being tracked

~0.7% of activity

local Zone = require(script.Parent.Zone)
local zone = Zone.new(script.Parent.Hitbox)

for i = 1, 10000 do
	local part = Instance.new("Part")
	part.Name = "Part"..i
	part.Anchored = true
	part.Parent = workspace
	zone:trackItem(part)
	print("track item!")
end

zone.itemEntered:Connect(function(item)
	print("entered: ", item.Name)
end)

zone.itemExited:Connect(function(item)
	print("exited", item.Name)
end)

Obviously a real game environment won’t have completely static and anchored parts, and performance stats will vary from device-to-device when measured in studio, although that helps to give a basic gauge.

We use ZonePlus with minimal impact on memory for the games I work with, some for instance with 100 player servers with constantly changing characters. You’ll be fine working with plenty of parts and players, just aim to use item and player events where possible as these are the most optimal.

If a part being tracked is destroyed (i.e. part:Destroy()) then yep it is automatically internally untracked:

3 Likes

Ah, good to know. I was somewhat worried because some levels in my game could potentially have excessive numbers of object requiring tracking, and its highly unlikely that would exceed 10,000!

Side note: after trying out the example you gave above using a counter instead of :getParts(), it seemed to get confused if the player was to jump repeatedly on the zone. Sometimes the player would be stood inside and there would be no output until the player leaves and enters again.

This isn’t an issue for me anymore as I’ve decided to go down the :trackItem() route instead, but I thought it was worth mentioning. I think it may be something similar to issues mentioned a while ago with moving between zones quickly (i think it was with the 2 post process changing zones that were touching in the example place) but I could be completely wrong.

The default accuracy is ~0.1 seconds (although can be slightly longer with part events), so it sounds like the character may just be falling back down quicker than it takes for the zone to detect it outside.

There have been some limiting behaviours with the part events so we’ll likely recommend people to use the item events instead where possible in the next doc update. Feel free post more bug reports though if you find precise and reproduceable bugs with the part events.

2 Likes

After setting accuracy to Precise, it does seem to have just been what you said there.
If I find anything else I won’t hesitate to report them here!

Also, how often does the Precise accuracy check for overlaps? I know High is 10 per second, is precise per frame? After checking the accuracy Enums and seeing that the cooldown time is 0 seconds I can answer that for myself lol.
If precise is used on the client, and the client uses an fps unlocker, will that make the updates more frequent or is it tied to heartbeat / stepped?

1 Like

Checks are tied to heartbeat :+1:

2 Likes

This module can be used for swords or any weapon?, or is it not recommended?

ZonePlus is primarily intended for static items, although you could achieve this by setting accuracy to "Precise".

I’d definitely recommend checking out other resources like Raycast Hitbox and FastCast as these will be more optimal for weapons / frequently moving objects.

4 Likes

@ForeverHD Do you think it would be possible to get rid of most dependencies in the future? I think it would be beneficial for most of us, thank you :)!