How can I find the the closest NPC by getting the closest path?

As you can see by the title, how can I find the the closest NPC by getting the closest path?

I tried doing this by getting the least amount of waypoints to somehow get the closest path, but it always prints Player1 no matter if I move it or not.
Code:

local AI = script.Parent
local Humanoid = AI:WaitForChild("Humanoid")
local HumanoidRootPart = AI:WaitForChild("HumanoidRootPart")
local pathdistances
for i, v in pairs(workspace:GetChildren()) do
	if v:IsA("Model") then
		if string.sub(v.Name, 1, 6) == "Player" then -- all the npcs are named player1, 2, 3, and so on
			local hrp = v:WaitForChild("HumanoidRootPart")
			local PathfindingService = game:GetService("PathfindingService")
			local path = PathfindingService:CreatePath()
			path:ComputeAsync(HumanoidRootPart.Position, hrp.Position)
			pathdistances = {
				
			}
			local waypoints = path:GetWaypoints()
			pathdistances[v.Name] = #waypoints -- number of waypoints
			
			print(#waypoints)
		end
	end
end	

local smallestValue, smallestKey  = math.huge 

for key, value in pairs(pathdistances) do
	if value < smallestValue then
		smallestKey, smallestValue = key, value
	end
end

if smallestKey then
	print("The closest player is "..smallestKey) -- prints Player1...?
end

image of all the NPCs and AI (ai is blue)

how can I make it prints player3 pls help thanks

Hmm, looks like you are finding the smallest number of waypoints since path distances is the #waypoints which is why you are getting player 1?

Maybe try finding the distance of each path by summing up the waypoint to waypoint vectors instead to find total path distance instead.

Edit: Yeah this image should help illustrate, the amount of waypoints generated by the path finding

player3 has the least wayponts though, why should i do it this way

Because theoretically, it should work.

If you see the path finding waypoint image on the Roblox waypoint website you can see that the points are not evenly spaced with each other and that some points are close to each other to the point of overlapping.

Assuming this is true, that means it is wrong to assume the number of waypoints equals the closest path as the distance between waypoints is not the same.

Are you sure player 3 has the least amount of way points though? If so then why are you getting player1 instead:

Edit: Also the path finding system can be wonky at times, if you have played several zombie shooter games you can notice the zombies climbing up on top of each other.

Edit: Also try looking at this, someone else has tried a similar problem… NVM I don’t think its related

Okay, I see what you’re saying. I made a script:

local AI = script.Parent
local Humanoid = AI:WaitForChild("Humanoid")
local HumanoidRootPart = AI:WaitForChild("HumanoidRootPart")
local pathdistances
local lastposition = nil
local distance = 0
for i, v in pairs(workspace:GetChildren()) do
	if v:IsA("Model") then
		if string.sub(v.Name, 1, 6) == "Player" then -- all the npcs are named player1, 2, 3, and so on
			local hrp = v:WaitForChild("HumanoidRootPart")
			local PathfindingService = game:GetService("PathfindingService")
			local path = PathfindingService:CreatePath()
			path:ComputeAsync(HumanoidRootPart.Position, hrp.Position)
			pathdistances = {
				
			}
			local waypoints = path:GetWaypoints()
			for index, waypoint in pairs(waypoints) do
				if index ~= #waypoints then
					if lastposition ~= nil then
						distance = distance + (lastposition - waypoint.Position).Magnitude
						lastposition = waypoint.Position
					else
						lastposition = waypoint.Position
						distance = (lastposition - Vector3(0, 0, 0)).Magnitude
					end
				else
					lastposition = nil
					pathdistances[v.Name] = distance
					distance = 0
				end
			end
		end
	end
end	

local smallestValue, smallestKey  = math.huge 

for key, value in pairs(pathdistances) do
	if value < smallestValue then
		smallestKey, smallestValue = key, value
	end
end

if smallestKey then
	print("The closest player is "..smallestKey)
end

Output: Workspace…AI:25: attempt to call a table value

Nervmind, fixed it by doing .new, but it still says Player1…

Make sure to reset the current distance for each pathfinding path

            local distance = 0
			for index, waypoint in pairs(waypoints) do
				if index ~= #waypoints then
					if lastposition ~= nil then
						distance = distance + (lastposition - waypoint.Position).Magnitude
						lastposition = waypoint.Position
					else
						lastposition = waypoint.Position
						distance = (lastposition - Vector3(0, 0, 0)).Magnitude
					end
				else
					lastposition = nil
					pathdistances[v.Name] = distance
					distance = 0
				end
			end

Edit: NVM, I think you did that…

Already did, I wonder why it’s doing that.

Hmm, maybe its the sum?

try this to sum up the way point vectors to find distance

local currentTotalDistance = 0
for i=1, #waypoints-1,1 do

local vectorToNextWayPoint = waypoints[i+1]-waypoints[i]

local distanceToNextWayPoint = vectorToNextWayPoint.Magnitude

currentTotalDistance += distanceToNextWayPoint

end

--Then store the distance in a table like you were doing before
local storeLoL = {currentTotalDistance}

also, when i remove player 1 from workspace, it says player2

Hmm, in difficult situations like these I suggest visually debugging your code since it might be the pathfinding algorithm.

Try creating a part at each waypoint of the same color and do some print debugging as well.

Howdy, the method of determining which one is closest by figuring out which one has the lowest number of waypoints should work. Add a print statement to every for loop and try to gather as much information as you can from them. Ill edit this post if I come up with a solution, I am testing your problem in my own studio at the moment.

1 Like

Hey guys! Thanks so much for helping me, but I have found the problem. I was resetting the table everytime I looped to the next NPC.

Script:

local AI = script.Parent
local Humanoid = AI:WaitForChild("Humanoid")
local HumanoidRootPart = AI:WaitForChild("HumanoidRootPart")
local pathdistances
local lastposition = Vector3.new(0,0,0)
local distance = 0
local pathdistances = {
}
for i, v in pairs(workspace:GetChildren()) do
	if v:IsA("Model") then
		if string.sub(v.Name, 1, 6) == "Player" then -- all the npcs are named player1, 2, 3, and so on
			local hrp = v:WaitForChild("HumanoidRootPart")
			local PathfindingService = game:GetService("PathfindingService")
			local path = PathfindingService:CreatePath()
			path:ComputeAsync(HumanoidRootPart.Position, hrp.Position)
			local waypoints = path:GetWaypoints()
			for index, waypoint in pairs(waypoints) do
				
				if index ~= #waypoints then
					distance = (waypoint.Position - lastposition).Magnitude
					lastposition = waypoint.Position
				else
					distance = (waypoint.Position - lastposition).Magnitude
					lastposition = Vector3.new(0,0,0)
					pathdistances[v.Name] = distance
					distance = 0
				end
			end
		end
	end
end	

local smallestValue, smallestKey = math.huge -- The largest number Lua can represent, just makes the loop check code easier to write

for key, value in pairs(pathdistances) do
	if value < smallestValue then
		smallestKey, smallestValue = key, value
	end
end

if smallestKey then
	print(smallestKey)
end




1 Like