Why does my NPCs go off path?

In my game there are NPCs walking around, using pathfinding service. But for some reason when I rerun the pathfiding on the path.Blocked event it goes back and forward from the new path.

This is the code that’s in control of moving the NPC’s

function MoveNPC(npcPart, npcPathFolder)
	local waypoints = npcPathFolder:GetChildren()
	local agentParams = nil

	if Settings["TowerNPCNames"][npcPart.Name] then
		agentParams = Settings["TowerAgentParams"]
	else
		agentParams = Settings["AgentParams"]
	end

	local currentWaypointIndex = 1

	local currentWaypoint = waypoints[currentWaypointIndex]

	local nextWayPointIndex
	local reachedConnection
	local blockedConnection

	local Walkspeed = npcPart:GetAttribute("Walkspeed")
	local animator = Instance.new("Animator", npcPart.Humanoid)
	local idleAnimation = animator:LoadAnimation(ReplicatedStorage.Storage.Animations.Idle)
	local runAnimation = nil

	if Walkspeed < 16 then
		runAnimation = animator:LoadAnimation(ReplicatedStorage.Storage.Animations.Walk)
	elseif Walkspeed >= 16 then
		runAnimation = animator:LoadAnimation(ReplicatedStorage.Storage.Animations.Run)
	end

	local idleAnimation2 = animator:LoadAnimation(ReplicatedStorage.Storage.Animations.Idle2)
	idleAnimation.Priority = Enum.AnimationPriority.Idle
	idleAnimation2.Priority = Enum.AnimationPriority.Idle
	runAnimation.Priority = Enum.AnimationPriority.Movement
	idleAnimation:Play()

	local controller = npcPart:FindFirstChildOfClass("Humanoid")

	local humanoid = npcPart:FindFirstChild("Humanoid")
	humanoid.WalkSpeed = Walkspeed

	for i, v in pairs(npcPart:GetDescendants()) do
		if v:IsA("BasePart") then
			v:SetNetworkOwner(nil)
		end
	end

	local function getPath(destination)
		local path = getPathCache(npcPart.PrimaryPart.Position, destination.Position, agentParams)

		if npcPart ~= nil and npcPart:FindFirstChild("HumanoidRootPart") then
			Paths[npcPart] = path
			path:ComputeAsync(npcPart.PrimaryPart.Position, destination.Position)
		end

		return path
	end

	local function walkTo(destination)
		local path = getPath(destination)

		if path.Status == Enum.PathStatus.Success then
			if Settings["VisualizePath"] then
				if not workspace.VisualPoints:FindFirstChild(npcPart.Name) then
					local folder = Instance.new("Folder")
					folder.Name = npcPart.Name
					folder.Parent = workspace.VisualPoints
				else
					workspace.VisualPoints:FindFirstChild(npcPart.Name):Destroy()
					local folder = Instance.new("Folder")
					folder.Name = npcPart.Name
					folder.Parent = workspace.VisualPoints
				end

				local function getRandomNumber(minNumber, maxNumber)
					return math.floor(math.random(minNumber, maxNumber))
				end

				if not PathColors[npcPart.Name] then
					PathColors[npcPart.Name] = Color3.fromRGB(getRandomNumber(0, 255), getRandomNumber(0, 255), getRandomNumber(0, 255))
				end
				local pathColor = PathColors[npcPart.Name]

				for index, waypoint in pairs(path:GetWaypoints()) do
					local visualPart = Instance.new("Part")
					visualPart.Anchored = true
					visualPart.Size = Vector3.new(1,1,1)
					visualPart.Color = pathColor
					visualPart.CanCollide = false
					visualPart.CanTouch = false
					visualPart.Parent = workspace.VisualPoints:FindFirstChild(npcPart.Name)
					visualPart.Position = waypoint.Position
				end
			end

			-- Connect the Blocked event
			blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
				blockedConnection:Disconnect()
				walkTo(destination)
			end)

			for index, waypoint in pairs(path:GetWaypoints()) do
				humanoid:MoveTo(waypoint.Position)
				humanoid.MoveToFinished:Wait()
			end
			
			blockedConnection:Disconnect()
		else
			if npcPart ~= nil and npcPart:FindFirstChild("HumanoidRootPart") then
				runAnimation:Play()
				humanoid:MoveTo(destination.Position - (npcPart.HumanoidRootPart.CFrame.LookVector * -10))
				task.wait(2)
				runAnimation:Stop()
			end
		end
	end

	local function Patrol()
		local currentWaypoint = waypoints[currentWaypointIndex]
		if not npcPart:FindFirstChildOfClass("Humanoid") then return end

		if not runAnimation.IsPlaying and npcPart.Humanoid.WalkSpeed > 0 then
			runAnimation:Play(.1)
		end


		walkTo(currentWaypoint)

		if currentWaypoint:FindFirstChild("WaitTime") then
			idleAnimation:Stop(0.4)
			local WaitTimeValue = currentWaypoint.WaitTime.Value
			runAnimation:Stop(0.1)
			idleAnimation2:Play()
			task.wait(idleAnimation2.Length)
			idleAnimation2:Stop(0.4)
			idleAnimation:Play()
			task.wait(WaitTimeValue - idleAnimation2.Length)
		end

		currentWaypointIndex = currentWaypointIndex + 1
		if currentWaypointIndex > #waypoints then
			currentWaypointIndex = 1
		end
	end

	while task.wait() do
		Patrol()
	end
end
3 Likes

Looks like you arent breaking out of the waypoint loop on blocked. Try something like this:

local blocked = false
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
	blockedConnection:Disconnect()
	blocked = true -- Be aware, this blocked connection might fire even if the path that was already walked was blocked. Be sure to check the index here.
	walkTo(destination)
end)

for index, waypoint in pairs(path:GetWaypoints()) do
	if blocked then break end 
	humanoid:MoveTo(waypoint.Position)
	humanoid.MoveToFinished:Wait()
end
4 Likes

Now it does this

2 Likes

Get rid of the walkTo call in the blocked event since you are already calling it in patrol.

Now it goes back to the previous waypoint before continuing the path

1 Like

I believe this has something to do with the fact that your Patrol function resets the currentWaypointIndex to 1 causing it to go back to index 1 and then to index 2.

Try moving the blocked variable to the top of your MoveNPC function, change blocked to false just before starting the walkTo function in Patrol and then change blocked to true when the path is blocked. Finally, before incrementing the currentWayPointIndex, check if not blocked.