Most performant way to detect players in a radius

I’m looking for a performant way to detect mass amounts of players in a radius. First thing that came to mind when I was brainstorming was Roblox’s explosion instance. I had used it before for a similar purpose, and worked fine then. One concern about this method is how efficient it is. Would I be better off detecting players via Region3 or magnitude?

2 Likes

Region3 is deprecated. You should use worldroot:GetPartBoundsInRadius(position, radius, overlapParams). instead if you intend to follow that route.

function getRoots()
    local roots = {}
    for _,player in pairs(game.Players:GetChildren()) do
        if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
            table.insert(roots,player.Character.HumanoidRootPart)
        end
    end
    return roots
end

position = Vector3.new(0,0,0)
radius = 15
overlapParams = OverlapParams.new()
overlapParams.FilterType = Enum.FilterType.Whitelist
overlapParams.FilterDescendantsInstances = getRoots()

nearbyPlayers = workspace:GetPartBoundsInRadius(position, radius, overlapParams)

Or just iterate through game.Players and check magnitude. I don’t know how to answer which would be faster.

10 Likes

I believe Magnitude would work best for your purposes. Region3 can be quite costly for a lot of instances.

Wow I didn’t even realize Region3 was deprecated lol. I appreciate the answer, but I do know one thing. Iterating through a bunch of parts and calculating magnitude is not efficient if your aoe is small. workspace:GetPartBoundsInRadius() is likely more performant in everyway, yet that doesn’t answer if roblox’s Explosion instance is less performant.

It’s actually pretty efficient on the client and doesn’t cause any noticeable lag. You should do some benchmarking tests and see which one fits better.

I’ve read somewhere that iterating through every part in the workspace at once and calculating magnitude is definitely not as performant as some other methods.
I got the explosion idea from:

Here is where I read about different use cases:

I’m performing aoe attack checks on the server for context. Because the aoe isn’t very big, continuing with roblox’s explosions should be performant, but I don’t know exactly how roblox explosions detect parts in the first place.

I’ll probably end up doing that anyways, but I was curious to see if anyone on the forum could just outright tell me lol

Decided to go with this method anyways because roblox explosions require you to listen for the hit event (I prefer to do everything in one thread)

Explosions have a BlastRadius property already, there’s no need to reinvent the wheel. Additionally, RBXScriptConnection objects (connections) made from its Hit RBXScriptSignal object (event/signal) pass the hit distance, that is, the distance between the explosion’s position and the hit part’s position to any connected callback function(s).

local Workspace = workspace

local function OnHit(HitPart, HitDistance) --Connected callback function is passed hit part and hit distance as arguments.
	--Perform desired code.
end

local Explosion = Instance.new("Explosion")
Explosion.Position = Vector3.new(0, 0, 0) --Specify a 3D environment position.
Explosion.Hit:Connect(OnHit) --Create connection to 'Hit' event/signal.
Explosion.Parent = Workspace
1 Like

You dont need to iterate through “every part in the workspace” just iterate through the player list and get the players’ character models. Then do a simple distance check from the character to the point using .Magnitude. Almost certain that would be the best option here.

Doing this is pretty simple.

There are two methods

  1. Radius
  2. GetPartsInPart or GetPartsBoundInRadius

If your explosions are in the shape of a sphere, I would use Radius/Magnitude as it is very lag efficient. To do this as optimally as possible, I would use CollectionService to tag all humanoid root parts in the game (npc and player)

local CollectionService = game:GetService("CollectionService")

local function getEntitiesInRadius(origin, radius)
	local entities = {}
	
	local rootParts = CollectionService:GetTagged("HumanoidRootParts")
	for i, part in pairs(rootParts) do
		if (part.Position - origin) <= radius then
			table.insert(entities, part.Parent) --Inserting the character into the 'entities' table
		end
	end
	
	return entities
end

local entities = getEntitiesInRadius(character.HumanoidRootPart.Position, 30) --Get entities within 30 studs

I would not use roblox’s Explosions as you are more limited to what you can do, and it is not as optimal as this method.

1 Like

Yes, I understood how roblox explosions worked from the getgo, but I wanted to know if they had any performance drawbacks or drawbacks at all.

Iterating through only the player list doesn’t account for NPC’s nor’ Hurtboxes. It’s imperative that I know which part was hit because I need to know if it was a Hurtbox or not. GetPartBoundsInRadius allows me to only get the parts in that region, so I only have to iterate through a few parts compared to the entire workspace.

Yeah, I stopped using explosions when I realized I have to pass arguments from the Hit Event. I ended up going with GetPartBoundsInRadius because the area i’m detecting is fairly small.

workspace:GetPartBoundsInRadius can get kinda slow if you use a large radius I would benchmark it with looping every player and doing a magnitude check because I believe that can be faster then workspace:GetPartBoundsInRadius specially if your using a large radius