Detecting Players in Multiple Radiuses

The Topic: I’m working on a city-RPG game that was inspired by an old game, with new mechanics as part of the role. One of the features includes a place indicator that tells the player where they are at, in different locations of the entire map.

The basic idea I had to achieve this was to consider using radius and magnitude and have at least three different locations that will tell the player their current general location.

I use a table to keep a list of the locations around the map as a starter.

For example, when a player enters LocationB from LocationA, a GUI will appear with the name of that location.

The Actual Issue:

--RAN FROM A LOCALSCRIPT
local InZone = false

local function CheckZone(Zone)
	if (Zone[1] - LocalPlayer.Character.HumanoidRootPart.Position).magnitude <= Zone[2] then
		if not InZone then
			InZone = true
			M.ZoneAppear(Zone[3],0.75,Zone[4]) -- passed parameters
		elseif InZone then
			InZone = false  -- start of the issue
		end
	end
end

local CheckWait = coroutine.wrap(function()
	while true do
		R.Heartbeat:Wait() -- if the player was in LocationA:
		CheckZone(Zones[1])  -- LocationA, returns true, fires the function
		CheckZone(Zones[2])  -- LocationB, false
		CheckZone(Zones[3])  -- LocationC, false
	end
end)	
CheckWait()

When CheckZone() runs with a chosen zone, it will check if the Player’s HumanoidRootPart is within the box. If it is within the box, InZone will be true, and a function from a ModuleScript will fire with passed parameters from that table. However, if it detects that it is NOT within the box, then it will set InZone to false.

However, the same function will fire with the other zones, and they will set InZone to false too.

At the speed of the server, CheckWait() will do this if the player is in LocationA:

  1. CheckZone(LocationA) will say it’s true and fire.
  2. CheckZone(LocationB) and (LocationC) will say it’s false and doesn’t fire.
  3. Repeat the same three functions.
    End Result: The function from the ModuleScript will keep firing over and over until the Player is not within any location.

Solutions attempted:
Before using radius, I gave it a shot at using the .Touched events from an invisible non-collidable part in the same three locations.

  1. The parts think that the HumanoidRootPart has left and re-entered, firing the function again. Again, I attempted with using a local boolean value. Caused by using a VehicleSeat.
  2. SpawnLocations tend to set the player’s location wayyyyy up high first. It’s not noticed due to a delaying loading screen, but it is a problem still.

The actual thing I wanted:
The function from the ModuleScript to fire only once when a player enters a Location.

I didn’t see any other topics that involved the use of more than one radius, but if I’m wrong, I don’t mind being linked to it.

Try this, I modified the code so that it activates the function only when the player enters a zone.
I also added the InZone variable that makes sure to activate the function too when the player moves instantly from one zone to a different zone (for example teleports or something).

local InZone = nil

local function CheckZone(Zone, send)
    if (Zone[1] - LocalPlayer.Character.HumanoidRootPart.Position).magnitude <= Zone[2] then
        if send or (InZone != Zone) then
            InZone = Zone
            M.ZoneAppear(Zone[3],0.75,Zone[4])
        end
        return true
    end
    return false
end

local previousState = false
local state = false
local CheckWait = coroutine.wrap(function()
	while true do
		R.Heartbeat:Wait() -- if the player was in LocationA:
        state = false
        for i = 1, #Zones do
            state = state or CheckZone(Zones[i], not previousState)
        end
        previousState = state
	end
end)	
CheckWait()
1 Like

The Module keeps firing the function. I’ll probably isolate the zone script on an empty baseplate and try to work out a solution from there, along with the modified code you provided me until I can throw it back into the game. Might be the size of the zones overlapping, but who knows. Thanks!

It’s most probable that the zones are overlapping.

Bullseye! I managed to figure out the small mistake in the code and also to prevent the module from firing over and over.

local function CheckZone(Zone, send)
    if (Zone[1] - LocalPlayer.Character.HumanoidRootPart.Position).magnitude <= Zone[2] then
        if send or (InZone == Zone) then  -- the small issue
            InZone = Zone
			print(LocalPlayer.Name.." Fire Module")
            M.ZoneAppear(Zone[3],0.75,Zone[4])
        end
        return true
    end
    return false
end

The code was trying to compare a variable to an entire table (within the main table), but by adding the 3rd item, the code was able to define whether it was matched or not. By not being able to find a match between the two variables, I assumed the local InZone variable was assigned with that same name. For future reference, when a player attempts re-entry, the code already knows the player is in and doesn’t fire.

Not to mention clearly, but let’s never forget that we can always make human errors, especially when I noticed the comparing arithmetic having != instead of ~=.

local Zones = {
	{
	workspace.ZoneA.Position,
	workspace.ZoneA.Size.Magnitude,
	"Centre of Dystokia", -- 3rd item called for comparison
	Color3.fromRGB(255,255,50)},
...

I think that clears up the whole issue of multiple radiuses and the module now. I took a bit of time to rewrite the code and managed to figure the issue out.

local function CheckIfPlayerInZone(ZoneData,Sent)
	if (ZoneData[1] - LocalPlayer.Character.HumanoidRootPart.Position).magnitude <= ZoneData[2] then
		if Sent or (IsInZone ~= ZoneData[3]) then
			IsInZone = ZoneData[3] -- remade entire code, thread now compares the variable
			M.ZoneAppear(ZoneData[3],0.75,ZoneData[4])
		end
		return true
	end
	return false
end

Other than that, thanks for your help!

Happens when you are programming in multiple languages and in most cases it’s ‘!=‘, sorry for that.

However you can still compare these tables as this code will compare their references whether they point at the same object in the memory, so there’s no need to compare the names as you implemented it.