Calculating Total Amount of Players in a Certain Area

Hey everyone, I’m Keanny. I’m the developer of my game called “Globetrotter” (which currently is in its alpha stage) and this question is related to the game itself.

  1. What do you want to achieve?
    Let’s say there is an area where players can enter. I would like to make it so that it counts the amount of players in said area and I want it to work on a regular script.

  2. What is the issue?
    I don’t know how to achieve such effect.

  3. What solutions have you tried so far?
    I know that Region3 can achieve such effect, but despite searching on the Dev Forums and on Youtube, I still see nothing close to what I need.

This should be simple as I see this kind of system being used all the time. Any help will be greatly appreciated.

-Keanny

8 Likes

This is indeed possible by using region3 and it’s a pretty simple process.

Firstly, you’ll need to construct your region3 to your area - if your can’t get the correct area use EgoMoose’s Rotated Region3 Module.

local region = Region3.new(Vector3.new(0,0,0), Vector3.new(5,0, 5))

Secondly, you’ll need to construct a table to hold every player in the area and start looping also use workspace:FindPartsInRegion3 / workspace:FindPartsInRegion3WithIgnoreList.

local t = {}
local players = game:GetService("Players") --we'll use this later

coroutine.wrap(function() --I like to coroutine wrap my loops
    while true do
        wait()
        local parts = workspace:FindPartsInRegion3(region)
        --I'll explain what to do here
    end
end)()

Thirdly, you’ll have to loop through ‘parts’ and find which ones belong to a player/ if they do.
Then insert the player into the table, before doing so make sure they aren’t already in the table.

for _, v in pairs(parts) do
    local plr = players:GetPlayerFromCharacter(v.Parent) --get the player
    if plr and not t[plr.UserId] then --did it get one and is it not in the table?
        t[plr.UserId] = plr --insert them
    end
end

Now you can use #t and it’ll be the amount of players in that area likewise you can loop through t and it’ll contain every player in the area.

2 Likes

as @return_end1 mentioned region3 can do the trick but what hasn’t been mentioned is that magnitudes work as well but for circular areas. by creating a module that contains something like so:

local module = {}

function module.GetPlayersInArea(pivot, radius)
   local playerList = {}
   for i,v in pairs(game.Players:GetPlayers())do
      local distance = v:DistanceFromPlayer(pivot)
      if distance <= radius then
         table.insert(playerList, v)
      end
   end
   return playerList
end

return module

this returns a full on list of the players (inside game.Players) that are within the given radius. keep in mind that doing it like this gives a Spherical radius effect.

the amount can simply be counted with

local list = module.GetPlayersInArea(pivot, radius)
local amount = #list
2 Likes

Ok, I used your code and there is a problem: The player wasn’t inserted into the table. How did I know this? Well, I added a print() that prints out the number of items in the table and it returned 0. Aren’t you supposed to use table.insert for that?

Aha, I just remembered that the # operator only includes numeric indexes.
Therefore, indeed we will need to use table.insert() but for checking that they’re not in the table we’ll have to use the new table function table.find()

Put that all together and your code should look like:

for _, v in pairs(parts) do
    local plr = players:GetPlayerFromCharacter(v.Parent)
    if plr and not table.find(t, plr) then 
        table.insert(t, plr)
    end
end

Ok, the code now works but when someone leaves the place the player list doesn’t remove said person. How should one achieve that?

I thought of a better idea, create it into a function and then you can call it to get the amount inside.
Might as well define t inside the function.

local players = game:GetService("Players")
local region = Region3.new(Vector3.new(0,0,0), Vector3.new(5,0, 5))

function getPlayersInRegion()
    local t = {}
    local parts = workspace:FindPartsInRegion3(region)
    for _, v in pairs(parts) do
        local plr = players:GetPlayerFromCharacter(v.Parent)
        if plr and not table.find(t, plr) then 
            table.insert(t, plr)
        end
    end

    return t
end

print( #getPlayersInRegion() )
5 Likes

Region3 works but it is also very laggy if you loop through the parts inside of the region. I think magnitude is your best bet, because Region3 is laggier and magnitude is easy and simple anyways.

3 Likes

@return_end1, your system works but there are some problems while implementing this into practical use (game wise).

  1. The Region3 does sense players alone, but when a player is in a car (which will definitely happen in my case), it doesn’t sense the player’s existence.

  2. I have the Region3 set up so that when there is a certain amount of players in said area, the race sequence starts. This is what I currently have:

while true do
	wait()
	amountOfPlayers = getPlayersInRegion3(region)
	print(amountOfPlayers)
	if amountOfPlayers >= threshold then
		print("activated")
		wait(30)
		
		script.Parent.Countdown:Play()
		script.Parent.Countdown.Ended:Wait()
		startRace()
	
		while raceInProgress == true do
			wait()
		end
	end
end

The above code works fine on its own but I would like to make it so that when a player leaves the area, the waiting sequence pauses. I don’t know how to achieve so.

  1. I have a dictionary table that tracks the amount of laps each player has done. Thus I added this code into the getPlayersInRegion3 function declaration:
for index = 1, #playerList do
	raceData[playerList[index] ]= 0
	print(raceData[playerList[index]])
end

This is my first time using dictionary tables. Each key stores a player’s name and his/her lap count. Every time a player has done a lap, the lap count of said player adds by 1.
Code:

raceData[part.Parent.Name] = raceData[part.Parent.Name] + 1

Both codes work individually, but when combined I get this error:
attempt to perform arithmetic on field '?' (a nil value)

Please help.
-Keanny

  1. Are any ancestries for the player changed? Something to do with where stuff are put in the hierarchy can change how it’ll work.

  2. I’d make another function to make checking if there’s enough players easier:

function check()
    local amountOfPlayers = #getPlayersInRegion3(region)
    return amountOfPlayers >= threshold
end

Now in the raceInProgress loop, you can use that function as one of the conditionals:

while true do
    wait()
    if check() then
       print("Activated!")
       wait(30)

       script.Parent.Countdown:Play()
       script.Parent.Countdown.Ended:Wait()
       startRace()

       while raceInProgress and check() do
           wait()
       end
   end
end
  1. playersList[index] would be a player, therefore to set it as their name you’d do:
raceData[playerList[index].Name] = 0
  1. No, when the player enters a car, both the player and the car remains direct parents of the workspace.
  2. That’s not what I mean. I would like to check the presence of players before the race starts. Here’s what I mean:
while true do
	wait()
	if checkPlayers() then
		print("activated")
		wait(30) -- player amount is checked here, if insufficient amount then pause intermission
		
		script.Parent.Countdown:Play()
		script.Parent.Countdown.Ended:Wait()
		startRace()
	
		while raceInProgress == true do
			wait()
		end
	end
end
  1. The code you gave me ended up working! Thanks!
  1. I’m not sure then :thinking:
  2. Ah, I see - perhaps create a incrementing number where a while loop breaks if it reaches it, it’ll stop incrementing if there ain’t enough:
local count = 0

while count <= 29 do
    local amount = #getPlayersInRegion3(region)
    if amount then
        count = count + 1
        wait(1)
    else
        wait()
    end
end 
  1. I think I have an idea. There is a function called FindPartsInRegion3WithWhitelist() which acts like a normal FindPartsInRegion3() but it searches for a certain type of part in a Region3. My guess is that by setting the whitelist to player parts only, the game won’t get carried away by the cars (I did some further testing and found out that as long as a car is in the Region3, our getPlayersInRegion3() function breaks completely)
  2. That is exactly what I needed. Thanks! But on a side note, my game is suffering from wait() function delays. For example, a wait(0.1) can take up to 7 seconds to work. Although it doesn’t affect every wait() function in the game (like the money display for example), it is severe enough to break systems. This is not just me, but there are other people who are experiencing this issue too. Do you know how to fix it?

EDIT: Ok quick SOS here, I added a reward system that rewards players money if they get top 3 in a race. Here’s the code:

function assignPlacement(player)
	if placement[1] == player then
		player.leaderstats.Money.Value = player.leaderstats.Money.Value + 50000 --problem lies here
end

This is what I get:
attempt to index field 'leaderstats' (a nil value)
It should work, I’ve checked the code along with a reference from some of my other working code. Is there anything I’ve mistakenly done?

  1. The loop of adding to the whitelist may be performance heavy, adding the car shan’t make a difference to the region3. I’d suggest trying to print the unpacked version of what FindPartsInRegion3 returns.
    If when the car is added, the player’s parts won’t show and you feel like this is unexpected behavior, file a bug report.

  2. wait is never precise nor does it always have expected behavior, there’s nothing much you can really do about it, you can read more here:

  1. Well I don’t spot any end for the if statement but I guess you forgot to include that.
    My only hypothesis is that leaderstats doesn’t exist yet, and therefore WaitForChild is needed.

Hey there! Sorry for the extremely late reply, I had been focusing on game beautifying and optimizing for the past months. And also super late happy new year and early happy chinese new year 2020!

  1. What do you mean by “unpacked”? I don’t quite understand.

  2. I found the reason why my game was so slow - there are 1000+ junk files in my game! Cleaned it out with RoDefender and now everything is ok! Even with all the realism I added into the game it still runs faster than before. Oh and also I changed some wait() functions to tick() and Heartbeat functions.

  3. That one was long fixed idk how I fixed it lol.

Hello, Happy New Year to you too!

Either table.unpack() or the roblox global unpack() can return all of the values in the array.
Here’s an example:

local myTable = {1, 2, 3}
local one, two, three = unpack(myTable)
print(one, two, three) --// 1 2 3
print(unpack(myTable)) --// Also works!

You can read more about it here:

Hello I have a smiliar problem but I want to be able to make only a certain amount of players allowed in a certain area. My zone already counts how many players are in that certain area and I just need to be able to know how to only allow a certain amount of players. Please check my post if you want to help.

Thanks!