QuickZone | 60 FPS with 1M+ Zones | Ultra Fast ZonePlus / SimpleZone Alternative

QuickZone v1.4.7

This release fixes discrepancies between the code, the types, and the docs.

Fixes

  • Observer: The Observer constructor no longer requires zones in its config.
  • Types: Added setAutoSyncRate to the QuickZone luau type.
  • Method names: Updated the docs and TypeScript types to use the actual method names (getZoneOfEntity and getZoneOfPlayer) instead of the wrong ones (getEntityInZone and getPlayerInZone).
3 Likes

QuickZone v1.4.8

This release fixes a cleanup issue when destroying tagged models.

Fixes

  • Zone.fromTag: Destroying a tagged model now properly cleans up all its descendant part zones, preventing observers from continuing to track them under deferred signal behavior.
2 Likes

QuickZone v1.5.0

This release adds support for MeshParts and Unions through the new Mesh shape type.

Additions

  • Zone: Added the Mesh shape type, which resolves against a part’s actual collision geometry instead of a bounding box. Zone.fromPart and the container constructors now detect MeshParts and Unions automatically and assign them this shape. Mesh zones require a BasePart reference to query against; zones without one fall back to Block.
1 Like

I recently implemented the latest version of the module that supports MeshParts. I seem to have run into a new issue though that I didn’t have with the older version. Now when multiple MeshParts overlap, if a player moves between the two without actually leaving the conglomerate zone, it still considers it as the player leaving and entering the zone. Is there a clear way to avoid this behavior in the newest version?

Thank you for the notice. I will try to replicate it tomorrow and see if I can fix it. In the meantime, if it is not too much trouble, use BaseParts for your zones instead.

When you use MeshParts in v1.5.0, QuickZone automatically detects it as the new Mesh shape type that we did not have before (it defaulted back to Block). This new shape uses a different (and new) algorithm compared to the other shapes, and apparently there’s a bug in this new code.

1 Like

Got it, I went with a separate solution where I absolutely needed MeshPart levels of precision for spatial queries. I’ve used this module for a long time and it’s been extremely useful to me. I’ll be sure to check if anything changes!

QuickZone v1.5.1

Fixes

  • Mesh Shape Zone: Overlapping MeshParts and Unions zones fired exit and enter events while an entity moved within them due to float error in GetClosestPointOnSurface which made the part randomly count as outside. Mesh zones now allow a small tolerance (0.01 studs) when checking whether a point is inside a part.

You should check out the latest release. It should be fixed now.

1 Like


i cant use the link you forgot to add one dot

I don’t know how that happened. I’ve updated it now.

1 Like

Thank you for this brilliant module, We transitioned to this system after I was having issues getting my custom zone module to work (i suck). This reliably tracks players no matter how late they join which i love the most.

Also love the dynamic zones, we use it for vehicle destruction collisions and keeping players on our boats.

1 Like

QuickZone Tutorial and Demonstration


Thanks you for making this video, Possible Panda!

Hello, I’ve run into an issue where having many zones in a small area causes strange behavior; there are also problems with zones failing to register zone entering (I have to leave and re-enter the zone). Here is my code snippet and a video of the problem:

do
	const coinObserver = QuickZone.Observer.new({
		groups = { playerGroup },
		zones = {},
		updateRate = 60,
	})
	
	const zoneSize = Vector3.new(20, 20, 20)
	
	const function collectCoin(player: Player, zone: QuickZone.Zone): nil
		-- TODO: Animation
		zone:getMetadata():Destroy()
		coinObserver:detach(zone)
		
		return nil
	end
	
	ChestSignals.CoinLanded:Connect(function(coin: MeshPart) 
		const newZone = QuickZone.Zone.new({
			cframe = coin.CFrame,
			size = zoneSize,
			metadata = coin,
			shape = "Ball",
			isDynamic = false,
		})
		coinObserver:attach(newZone)
		
		-- if player already in zone
		const player = coinObserver:getPlayersInZone(newZone)[1]
		if player then
			collectCoin(player, newZone)
		end
	end)
	
	coinObserver:observe(collectCoin)
end 

Video link: Unique Download Link | WeTransfer

Yes, you seem to have hit an edge case regarding observer:detach. I’ll patch it some time next week when I have more time, and the update will include quite a few other improvements. It will be a big update, but I’m busy with Rogen v2 and my internship at the moment.

For the time being, I think it should work if you do this:

do
	local coinObserver = QuickZone.Observer.new({
		groups = { playerGroup },
		updateRate = 60,
	})

	local zoneSize = Vector3.new(20, 20, 20)

	coinObserver:observe(function(player: Player, zone: QuickZone.Zone)
		local coin = zone:getMetadata()
		zone:destroy() -- not detach
		-- TODO: Animation
		coin:Destroy()
	end)

	ChestSignals.CoinLanded:Connect(function(coin: MeshPart)
		local zone = QuickZone.Zone.new({
			cframe = coin.CFrame,
			size = zoneSize,
			metadata = coin,
			shape = "Ball",
		})
		coinObserver:attach(zone)
		-- No "already in zone" check needed
	end)
end

thank you for help. Now all working!