Pathfinding System Cannot Avoid Obstacles

Hi, I’m trying to create a pathfinding system that moves towards the nearest player.
I have made the system and it works great!

But my problem is that attempting to use Humanoid:MoveToFinished:Wait() Will make the NPC I’ve made to go to the last waypoint in the last created waypoint and then move towards the player,

Resulting in the player being able to go studs away while the robot tries to catch up to the position you were when it reached the end of the final waypoint.

When Attempting to remove this event it just doesn’t avoid obstacles and instead tries to access the player through the wall, However when I added parts to each waypoint it showed that it sees the path to the player but chooses for some reason to ignore it.

Without MoveToFinished: (The lines indicated the path it sees but does not take)
image

With MoveToFinished: (The line indicates the path created by the pathfiningservice
image
As you can see it will first go to the end of the last waypoint and then go to the player, which is expected. But it’s not what I want.

I want the npc to be able to avoid obstacles while being able to follow the player’s position all the time.

I’ve tried these solutions:

Attempted to use path.Blocked (Which didn’t work + activated randomly)

Attempted to raycast to check if there’s a wall infront of the npc and then move him back
(Does not work as it cannot cancel the current moveto and if it does then it will just stop completly)

Attempted to use Path.Status which again did not work as it couldn’t detect the path being blocked because it calculated the path but still decided to go to the player via the wall so it didn’t think it was blocked.

Here is my full script:
It also includes the waypoint showcase script.

If you think you can fix this please try since I’ve been trying for a while now.

    -- SERVICES --
local pathfindingservice = game:GetService("PathfindingService")
local runservice = game:GetService("RunService")
--------------

-- VARIABLES -- 

local Entity = script:FindFirstAncestorOfClass("Model")
local PrimaryPart = Entity.PrimaryPart
local Humanoid = Entity:WaitForChild("Humanoid")

---------------

local pathparameters = {

	["AgentHeight"] = 12.6,
	["AgentRadius"] = 14.264

}

-- CHANGEABLE VALUES --

local killdistance = 4 -- The distance between the entity and the player which will then kill the player
local MinimumDistance = 100 -- The distance of which the entity can locate the player

-----------------------

PrimaryPart:SetNetworkOwner(nil)

function getnearestcharacter()
	local players = game.Players:GetPlayers()
	
	local target
	
	for _, player in players do
		
		if player.Character then
			
			local character = player.Character
			local humanoid = character.Humanoid
			
			if humanoid.Health > 0 then
				
				local distance = (PrimaryPart.Position - character.PrimaryPart.Position).Magnitude
				
				if distance <= MinimumDistance then
					
					target = character
					
				end
				
			else
				
				target = nil
				
			end
			
		end
		
	end
	
	return target
	
end


function attackplayer(target)
	local distance = (PrimaryPart.Position - target.PrimaryPart.Position).Magnitude
	local path = pathfindingservice:CreatePath(pathparameters)
	
	if distance < 4 then
		target.Humanoid.Health = 0
	else
		path:ComputeAsync(PrimaryPart.Position,target.PrimaryPart.Position,pathparameters)
		
		local waypoints = path:GetWaypoints()
		local waypointval = 1
		
		for i, waypoint in waypoints do
			
			local part = Instance.new("Part")
			part.Size = Vector3.new(1,1,1)
			part.Position = waypoint.Position
			part.Parent = game.Workspace
			part.Anchored = true
			part.CanCollide = false
			
			Humanoid:MoveTo(waypoint.Position)
			
		end
	end
	
end


function createpath(destination)
	
	local path = pathfindingservice:CreatePath(pathparameters)
	
	path:ComputeAsync(PrimaryPart.Position, destination)
	
	return path
	
end


function walktopath(destination)
	local path = createpath(destination)
	local waypoints = path:GetWaypoints()
	
		for _, waypoint in waypoints do
			local character = getnearestcharacter()
			
			if character then
				
				attackplayer(character)
				break
				
			else
				
				Humanoid:MoveTo(waypoint.Position)
				Humanoid.MoveToFinished:Wait()
				
			end
			
		end
	
end

function wander()
	
	local waypoints = workspace:WaitForChild("Waypoints"):GetChildren()
	
	for _, waypoint in waypoints do
		local randomwaypoint = math.random(#waypoints)
		walktopath(waypoints[randomwaypoint].Position)
	end
	
end



while true do
	wander()
end

Hastebin if it’s easier for you:

Please do not change anything in the script unless it’s about the problem it self, I don’t care if it’s not 100% efficient I just want it to work.

If you find a way to make my path finding smoother then sure you can add some edits but don’t change the names of the variables please.

You could possibly raycast and if it can directly see the player (if there is no wall infront) only then don’t use the wait event. If it can’t see the player directly then make a path to them. You can have issues with jumping though so another solution is getting the move direction of the player and then kind of guessing where the player will end up. And then make a path to that guessed position.

I’ve tried to create a raycast system like I’ve said,
Although in theory this should work the problem is that the attack script runs when the script finds a player.

So it will just stay stuck, Also I would need to set a lot of raycasts just to find the player as it would just not work when the player makes a turn.

Is there no way to just create the path whenever the player changes his location?
Or force the pathfinding service to follow each path even if it went to the end already?

Since from what I see the path finding system knows where to go but it doesn’t wait for the npc to get there before it already gets to the end.

The best solution would be making a system to keep up with the player. If you try to move it to the player’s position then it won’t keep up unless the NPC has really high walkspeed. The player can have pretty bad internet connection and that can make it worse or better for the NPC too.

I created the exact same thing. Here my script I used

local pathfindingservice = game:GetService("PathfindingService")
local human = script.Parent:WaitForChild("Humanoid")
local torso = script.Parent:WaitForChild("Torso")
local hf
local npc = script.Parent
local hastarget = false
local minDistance = math.huge
local cd = false
human.JumpPower = 50
local URadius = 2 -- radius of your NPC
local UHeight = 10 -- height of your NPC


local function targets()
	if cd == false then
		cd = true
		for _, player in pairs(game.Players:GetPlayers()) do
			if player.Character and player.Character:FindFirstChild("Torso") and player.Character:FindFirstChild("Humanoid").Health > 0 then
				Torso = player.Character:FindFirstChild("Torso")
				if Torso then
					if hastarget == true then
						minDistance = (torso.Position - target:WaitForChild("Torso").Position).magnitude
					end
					local distance = (torso.Position - Torso.Position).magnitude
					if distance >= 100 then
						human.WalkSpeed = 45
						
					elseif distance <= 30 then
						human.WalkSpeed = 16
						
					elseif distance <= 50 and distance > 30 then
						human.WalkSpeed = 30
						
					elseif distance <= 100 and distance > 50 then
						human.WalkSpeed = 40	
					end
					

					if distance < minDistance then
						print(minDistance)
						minDistance = distance
						target = player.Character
						print(player.Character)
						hastarget = true
					end
					
				end
			end
			
			return targets
		end
		cd = false
	end	
end
	
	


if hastarget == false then
	game.Players.PlayerAdded:Connect(function(player)
		player.CharacterAppearanceLoaded:Connect(function()
			targets()
		end)
	end)
end



while true do	
	
	
	if hastarget == true then
		
		
		
		
		local rnp = target:FindFirstChild("Torso").Position
		local thisone = Torso.Position - torso.Position
		local blacklist = RaycastParams.new()
		blacklist.FilterType = Enum.RaycastFilterType.Blacklist
		blacklist.FilterDescendantsInstances = {
			npc.HumanoidRootPart,
			npc.Head,
			npc["Left Arm"],
			npc["Left Leg"],
			npc["Right Arm"],
			npc["Right Leg"],
			npc.Torso
		}
		
		
		local ray = workspace:Raycast(npc.Head.Position, thisone, blacklist)
		
		
		
		if ray and ray.Instance.Parent == target or ray and ray.Instance.Parent.Parent == target then
			print("ray move")
			human:MoveTo(Torso.Position)	
			local ray2 = workspace:Raycast(npc.Torso.Position, npc.HumanoidRootPart.CFrame.LookVector * 5, blacklist)
			if (Torso.Position.Y - torso.Position.Y) > 5 and ray2 and ray2.Instance.Name == "Part" then
				human:ChangeState(Enum.HumanoidStateType.Jumping)
				print("JUMPING")
				wait(1)
			end
			targets()
		else
			
			local pathOptions = {
				URadius = URadius,
				UHeight = UHeight,
				SomeBooleanKey = true
			}
			--local path = pathfindingservice:CreatePath()
			local path = pathfindingservice:CreatePath(pathOptions)
			path:ComputeAsync(torso.Position, target:WaitForChild("Torso").Position)
			print("halfpoint")
			
			local waypoints = path:GetWaypoints()
			for _, point in pairs(waypoints) do
				print("pointmove")
				
				local howfar = (torso.Position - point.Position).magnitude
				
				if point.Action == Enum.PathWaypointAction.Jump then
					human:ChangeState(Enum.HumanoidStateType.Jumping)
					print("JUMPING")
				end
				
				
				human:MoveTo(point.Position)	
				
				human.MoveToFinished:Wait()
				local ray = workspace:Raycast(npc.Head.Position, thisone, blacklist)
				--if ray then
				--	print(ray.Instance)
				--end
		
				if ray and ray.Instance.Parent == target or ray and ray.Instance.Parent.Parent == target then
					
					print("broken")
					break
				end

				
				targets()
			end
			--local ray2 = workspace:Raycast(npc.Torso.Position, npc.HumanoidRootPart.CFrame.LookVector * 5, blacklist)
			--if (Torso.Position.Y - torso.Position.Y) > 5 and ray2 and ray2.Instance.Name == "Part" then
			--	human:ChangeState(Enum.HumanoidStateType.Jumping)
			--	print("JUMPING")
			--	wait(1)
			--end
		end
		
		
	end
		
	wait()
	
end