Zone+ | Retrieving players within an area/zone

Zone  banner

Zone+ is a lightweight application that utilises regions and raycasting to efficiently determine players within an area.


Theory

View

This kind of question has popped up a lot of times over the years so I thought I’d finally address it:

What’s the best method of retrieving players within an area/zone on the server?


Common solutions

  • "Using touched/untouched events!" - This was actually how I handled zone checking when I first began developing. Not only do these work incorrectly (untouched often fires before touched, untouched sometimes never fires at all when leaving a part, etc) but they can cause lag when complex shapes enter them.

  • "GetTouchingParts!" - The one major flaw with this function is it doesn’t work with uncancollided parts. If the player jumps off the part, they won’t be registered. buildthomas and evaera have created a solution for this if you’re interesting in exploring this path.

  • "Magnitude checks!" - Sorry circle worshippers, but the square lovers are going to have something to say here.

  • "Region3" (by itself) - Polgons hate him. Scientists at Cambridge university have found out… no.

  • "Raycasting" (by itself) - Fire ray below player’s character, check if part below, bam! Polygons and circles are happy bunnies now. Shame the server isn’t. This method is fine for small servers, but fails to scale effectively for servers with increasingly larger amounts of players with recursive checks.


The holy grail

Region3 + Raycasting

Method

  1. Setup a group of parts to represent your zone

  2. Calculate the maximum and minimum boundaries of this zone

  3. Create a Region3 value using these bounds

ᅠᅠᅠᅠᅠimage

  1. Get players in this region using FindPartsInRegion3. This function returns a list of parts, which you can use to check for a player’s character.

  2. For the players returned in the region check, fire a ray below from the HumanoidRootPart (not too far as this causes lag). If one of the zone’s parts is returned, we can safely say the player is within that zone.

    image

Benefits

  • No need to unnecessarily check every player within the server. Simply calculate a ‘rough’ area and determine who’s in the exact zone for players within that region.

  • Region3 and raycasting are ‘light-weight’. You can run these multiple times a second with minimal effect to performance.


Example 1 - Retrieving Players

local ZonePlus = require(4664437268) -- Initiate Zone+
local ZoneService = require(ZonePlus.ZoneService) -- Retrieve and require ZoneService
local group = workspace.YourGroupHere -- A container (i.e. Model or Folder) of parts that represent the zone
local zone = ZoneService:createZone("ZoneName", group, 15) -- Construct a zone called 'ZoneName' using 'group' and with an extended height of 15 

local playersInZone = zone:getPlayers() -- Retrieves an array of players within the zone

Example 2 - Entering/Exiting Events

local ZonePlus = require(4664437268)
local ZoneService = require(ZonePlus.ZoneService)
local group = workspace.YourGroupHere
local zone = ZoneService:createZone("ZoneName", group, 15)

zone.playerAdded:Connect(function(player)
	print(player.Name,"entered the zone!")
end)
zone.playerRemoving:Connect(function(player)
	print(player.Name,"exited the zone!")
end)
zone:initLoop()

Uses

For coded examples, visit the Zone+ Playground.

Safe Zone (using additionalHeight, a loop and 2000 randomly generated parts)

Safe Zone (using uncancollided parts and zone events)

Coin Spawner

Voting Pads


Resources

Zone+ is actively maintained by the HD Admin Team; feel free to report bugs, suggest features and make pull requests at our repository.


Credit

Special thanks to:

221 Likes

Thank you for this extremely useful bit of information! :grinning::smirk: “the holy grail” method is something I never even thought about using for finding players within a zone, I’ll be sure to use this a resource for future projects!

11 Likes

I’ve been looking for a good way to get players in a zone for a long time, thank you for this post! :grinning:

2 Likes

This is actually a smart way to go about it. It even supports arbitrary space, which is a huge plus point when working with regions.

This thread dismisses clunky methods and mini-wars about the best way to do this (especially wrt magnitude), then brings the two best methods to the table and has them cooperate: the basis being the raycast and the Region3 being a gatekeeping supportive arm.

Of course, naturally, not all other methods should be dismissed (actually just the last 3) as they do have utility in some cases. Majority of cases seem to be simple area/room scans though so that should be covered well enough by this.

Thank you for sharing. This thread will go a long way.

8 Likes

so, the best way to keep finding players in zone is using loop?

while wait(1) do
    for _,PlayersInZone in pairs(ZoneController:GetPlayersInZone(Workspace.Zone)) do
        print(PlayerInZone)
    end
end
2 Likes

Can you make a Demo place when they are in safe zone and giving them an ForceField and leaving safe zone removes ForceField.

For situations like the safe zone above, a loop works great, assuming you’re not checking too many times a second (a 0.5 second interval for example will be absolutely fine).

As a side-tip, you might want to check out the article colbert wrote on the While Wait-Do Idiom:

4 Likes

now this is epic. i’m most definitely going to use this in the future with my current project.

3 Likes

Interesting methodology! My own game uses GetTouchingParts to determine region occupation and I must say the fact that I didn’t see this simple yet elegant solution is almost insulting to me. I’ll definitely be upgrading my private system to make use of this method.

1 Like

Great post, this will work very well for general areas but it seems like it’d be rather tedious filling in boxes for more “natural” shapes/zones

Like have fun filling that with boxes :eyes:

(Not to state the obvious)

Another solution for more complex shapes to check if a point is inside a polygon is to simply raycast
from anywhere outside the polygon to your point and count how often it hits a side of the polygon.
(Odd # hits = inside). This shouldn’t be very performance intensive at all and should satisfy most use cases

4 Likes

Very much appreciated for making this, since touched/untouched events are a mess. Thank you for this!

1 Like

The method works the same for any type of shape. I believe you may be getting confused with the second image? Instead of creating lots of small boxes within the shape, we instead calculate a single Region3 determined upon the min and max bounds of the shape. Using this Region3, we then get all the players within the rough area, and fire a single ray below their HRP to confirm whether they are in the precise boundary we want to check for:

i.e. p3 and p4 will be returned

4 Likes

I meant making that shape out of parts is pretty tedious to do unless it’s hollow. But if it’s hollow firing a raycast downward won’t really be all that useful. I guess it wouldn’t be too tedious if you built its hollow shell and used some plugin to fill it in. I’m sure one exists

1 Like

Great system. I tested it out by creating a simple weather system.

17 Likes

Maybe you could add a CheckIfPlayerInZone() function?

1 Like

If the use case for this system isn’t key to gameplay, the ultimate solution would be to perform your downward raycast every frame on each of the clients for their respective characters, and when they hit a part identified as part of a zone, you can then fire a bindableevent and a remoteevent to cause stuff to happen both clientside and serverside. Clients could lie and fire the remoteevent when they aren’t actually in the zone, but for many applications, this isn’t important. For example, a zone that players must stand in if they want to participate in a minigame round, or a zone where stepping in it will apply weather effects (as seen in this thread).

If you want to further expand the utility of this module, you could add this clientside solution as a toggleable alternative, because for some requirements, the clientside method just makes more sense. For example, if someone wanted to open a shop UI upon the player entering a zone, it wouldn’t make sense to detect it on the server and then send a remoteevent signal to that player’s client.

1 Like

Do you have any plans on adding Added/Leaving events? Would be neat to have!

Nice idea! This will work effectively if developed with the correct sanity checks.

Absolutely, the article focuses on retrieving multiple players server-side so I haven’t worried about this. Firing a downward ray every x amount of time and checking this against a dictionary of parts is almost all you’ll need to do for the client.

Having a module work effectively for both client and server is definitely something I’d like to do in the future.

What would you be looking to achieve with this?

Personally I’d use this feature for the following:

  • Showing a 'Entered region_name’ or 'Left region_name’ UI
  • Updating a live vote count on voting pads upon player enter/leave
  • Initiating Cutscenes/camera movements, etc when a player enters a region

These things are currently a pain to do if you’re a part of circle gang (magnitude checks) or using .Touched.

1 Like

This is amazing, saves so much time on this new game, I’m working on for a road name system .

2 Likes