How to get closest part within a given radius that's also within view?

Hi. The title is self explanatory but just in-case I need to elaborate even more then:

My goal is to create an auto lock which locks onto an enemies humanoidrootpart so the player can teleport behind it. It is for a combat system. I would like to create an auto-lock on feature which gets the closest player root within a maximum radius which is also within camera view. Script examples would be appreciated although a simple and straight-forward explanation which be highly appreciative! :slight_smile:

I wrote a little local script for this, but this may be a bit laggy:

local UIS = game:GetService("UserInputService")
local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local camera = workspace.CurrentCamera
local Range = 20

UIS.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.E then
		for i,v in pairs(workspace:GetChildren()) do
			if v:IsA("Model") and v ~= Character and v:FindFirstChild("HumanoidRootPart") then
				local eHumRp = v.HumanoidRootPart
				local vector,onScreen = camera:WorldToScreenPoint(eHumRp.Position)
				if onScreen then
					if (Character.HumanoidRootPart.Position - eHumRp.Position).Magnitude < Range then
						print("done")
					end
				end
			end
		end
	end
end)

Also feel free to check my topics, they still don’t have a solution :stuck_out_tongue:

I read the code and it is partially what I want to achieve. I tested it in a local server of 3 players and it printed done twice when two players are within radius. What I would like to achieve is that out of the players within the radius it only gets the closest.

I would firstly recommend only looping through a folder of npcs and characters instead of the entire workspace

Secondly Id just use a simple minimum algorithm

local min = Range
local target = nil

--do all of the stuff to find the distance

--once you find the distance, store in variable distance
if distance < min then
    min = distance
    target = v --v is the character at the distance
end

--by the end of the entire for loop youll have the closest one
--you also dont have to check the range anymore, the min does that for you

It’d probably good to implement it so that the closest player to the player’s mouse is the one that’s selected, so long as they’re in the range. So maybe after the initial list of characters is created, you loop through one more time to find who is closest to the player’s mouse, ignoring the Z value since Magnitude already does that for us.

I looked at the answers of other guys and made a completely working script, you can make a remote event and run the server script, but don’t forget to make eChar nil after starting the event.

local UIS = game:GetService("UserInputService")
local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local camera = workspace.CurrentCamera
local Radius = 20
local Distances = {}
local eChar

UIS.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.E then
		for i,v in pairs(workspace:GetChildren()) do
			if v:IsA("Model") and v ~= Character and v:FindFirstChild("HumanoidRootPart") then
				local eHumRp = v.HumanoidRootPart
				local vector,onScreen = camera:WorldToScreenPoint(eHumRp.Position)
				if onScreen then
					local Distance = (Character.HumanoidRootPart.Position - eHumRp.Position).Magnitude
					if Distance < Radius then
						table.insert(Distances,Distance)
						local min = math.min(table.unpack(Distances))
						if min == Distance then
							eChar = v
						end
					end
				end
			end
		end
		table.clear(Distances)
		-- Your code goes here
		print(eChar)
	end
end)