Weird Pathfinding Bug

I keep getting this bug where the mob will just move back to its original position, here is an example video:

And here is my code in question:

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local MobPath = PathfindingService:CreatePath()

local NextWaypointIndex = 0
local CurrentPlayer
local ReachedConnection
local BlockedConnection
local ATTACKING_RANGE = 40

local function FindNearestPlayer(Character)
	local HumanoidRootPart = Character.HumanoidRootPart
	
	local Distances = {}
	local PlayerMapSnapshot = {}
	
	for _, Player in pairs(Players:GetPlayers()) do
		local PlayerCharacter = Player.Character or Player.CharacterAdded:Wait()
		local PlayerHumanoidRootPart = PlayerCharacter.HumanoidRootPart
		
		PlayerMapSnapshot[Player] = (PlayerHumanoidRootPart.CFrame.Position - HumanoidRootPart.CFrame.Position).Magnitude
		table.insert(Distances, PlayerMapSnapshot[Player])
	end
	
	for Player, Distance in pairs(PlayerMapSnapshot) do
		if Distance == math.min(unpack(Distances)) and Distance <= ATTACKING_RANGE then
			CurrentPlayer = Player
			return Player
		end
	end
	
	print("No distance and player were found.")
	return nil
end

local function TracePath(Character)
	local Humanoid = Character.Humanoid
	local HumanoidRootPart = Character.HumanoidRootPart
	local NearestPlayer = FindNearestPlayer(Character)
	
	if NearestPlayer == nil then
		if CurrentPlayer ~= nil then
			NearestPlayer = CurrentPlayer
		end
	end
	
	-- Precautionary check
	if NearestPlayer then
		local NearestCharacter = NearestPlayer.Character
		local NearestHumanoidRootPart = NearestCharacter.HumanoidRootPart
		
		local success, errormessage = pcall(function()
			MobPath:ComputeAsync(HumanoidRootPart.CFrame.Position, NearestHumanoidRootPart.CFrame.Position)
		end)
		
		if success and MobPath.Status == Enum.PathStatus.Success then
			local Waypoints = MobPath:GetWaypoints()
			
			BlockedConnection = MobPath.Blocked:Connect(function(BlockedWaypointIndex)
				if BlockedWaypointIndex > NextWaypointIndex then
					BlockedConnection:Disconnect()
					TracePath(Character)
				end
			end)
			
			if not ReachedConnection then
				ReachedConnection = Humanoid.MoveToFinished:Connect(function(Reached)
					if Reached and NextWaypointIndex < #Waypoints then
						NextWaypointIndex += 1
						Humanoid:MoveTo(Waypoints[NextWaypointIndex].Position)
					else
						ReachedConnection:Disconnect()
						BlockedConnection:Disconnect()
					end
				end)
			end
			
			NextWaypointIndex = 2
			Humanoid:MoveTo(Waypoints[NextWaypointIndex].Position)
		end
	end
end

local PathTracing = {}

function PathTracing.Init(Character)
	while true do
		task.wait(1)
		TracePath(Character)
	end
end

return PathTracing

And this module script has pieces from the Roblox Documentation Hub, and this module is cloned, required, and initialized inside the player.

I’m not sure what’s causing this though, it might be the ComputeAsync part, but I’ll be open to issues that may be the bug, and if it’s the cause, I’ll be fixing it in no time.

Update:

I fixed this bug by rewriting it. Here is the new version:

local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local MobPath = PathfindingService:CreatePath()

local NextWaypoint = 0
local ReachedConnection
local TargetMovedConnection
local PreviousPlayer
local ATTACKING_RANGE = 40
local MINIMUM_DISTANCE = 5

--[[
	1. Get nearest player or current player
	2. Use pathfindingservice to calculate routes
]]

local function Reset()
	ReachedConnection:Disconnect()
	TargetMovedConnection:Disconnect()
	
	ReachedConnection = nil
	TargetMovedConnection = nil
end

local function FindNearestPlayer(Character)
	local HumanoidRootPart = Character.HumanoidRootPart
	
	local Distances = {}
	local PlayerMapSnapshot = {}
	
	for _, Player in pairs(Players:GetPlayers()) do
		local PlayerCharacter = Player.Character or Player.CharacterAdded:Wait()
		local PlayerHumanoidRootPart = PlayerCharacter.HumanoidRootPart
		
		PlayerMapSnapshot[Player] = (PlayerHumanoidRootPart.CFrame.Position - HumanoidRootPart.CFrame.Position).Magnitude
		table.insert(Distances, PlayerMapSnapshot[Player])
	end
	
	for Player, Distance in pairs(PlayerMapSnapshot) do
		if Distance == math.min(unpack(Distances)) and Distance <= ATTACKING_RANGE then
			return Player
		end
	end
	
	return nil
end

local function FollowPlayer(Character)
	local Humanoid = Character.Humanoid
	
	if Humanoid and Humanoid.Health > 0 then
		local TargetPlayer = FindNearestPlayer(Character)
		
		if TargetPlayer then
			PreviousPlayer = TargetPlayer
			
			local TargetCharacter = TargetPlayer.Character
			local TargetHumanoid = TargetCharacter.Humanoid
			
			local success, errormessage = pcall(function()
				if TargetHumanoid and TargetHumanoid.Health > 0 then
					MobPath:ComputeAsync(Character.PrimaryPart.CFrame.Position, TargetCharacter.PrimaryPart.CFrame.Position)
				end
			end)
			
			if success and MobPath.Status == Enum.PathStatus.Success then
				local Waypoints = MobPath:GetWaypoints()
				
				if not ReachedConnection then
					NextWaypoint = 2
					
					if Waypoints[NextWaypoint] then
						Humanoid:MoveTo(Waypoints[NextWaypoint].Position)
					end
					
					ReachedConnection = Humanoid.MoveToFinished:Connect(function(Arrived)
						if Arrived and NextWaypoint < #Waypoints then
							NextWaypoint += 1
							
							if Waypoints[NextWaypoint] then
								Humanoid:MoveTo(Waypoints[NextWaypoint].Position)
							end
						elseif Arrived and NextWaypoint >= #Waypoints then
							if Waypoints[NextWaypoint] then
								Humanoid:MoveTo(Waypoints[NextWaypoint].Position)
							end
							
							Reset()
							
							if Humanoid.Health > 0 then
								FollowPlayer(Character)
							end
						end
					end)
				end
				
				if not TargetMovedConnection then
					TargetMovedConnection = TargetHumanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
						if TargetHumanoid.MoveDirection.Magnitude > 0 then
							Reset()
							FollowPlayer(Character)
						end
					end)
				end
			end
		end
	end
end

local PathTracing = {}

function PathTracing.Init(Character)
	pcall(function()
		repeat
			task.wait(1)
			FollowPlayer(Character)
		until Character:FindFirstChildOfClass("Humanoid").Health <= 0
	end)
end

return PathTracing

This is just like the code earlier but the initial movement is run in the if statement of whether or not reached connection exists, thus eliminating the bug of it coming back to its original position.

Now, this works the first time the mob loads but when it dies, a bug happens, and the movement is very choppy (sometimes this doesn’t happen, very selective movement is why this happens). Here is the bug output:

 Humanoid is not a valid member of Model "TestDummy"  -  Studio
  22:25:49.887  Stack Begin  -  Studio
  22:25:49.894  Script 'Workspace.Mobs.TestDummy.PathTracing', Line 51 - function FollowPlayer  -  Studio
  22:25:49.894  Script 'Workspace.Mobs.TestDummy.PathTracing', Line 103  -  Studio
  22:25:49.894  Stack End  -  Studio

Is there any solution for this as it makes no sense why this happens after all those if statements from the code earlier, also the choppiness. If you’d like a video, I’ll provide, but I think you get what I mean by this.

So I’ve rewritten my code to adjust to my needs, I’d say it’s a massive rewrite as most of my new code is entirely different compared to my code that I’ve posted almost a week ago. Here is my new code:

local Players = game:GetService("Players")
local PathfindingService = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")

-- Configuration
local ATTACKING_RANGE = 40

-- Private functions
local function GetNearestPlayer(Character)
	local HumanoidRootPart = Character.HumanoidRootPart
	
	--[[
		One is an array, so that
		we can use math.min to get the nearest distance
		number.
	]]
	local PlayerMapSnapshot = {}
	local Distances = {}
	
	for _, Player in pairs(Players:GetPlayers()) do
		local PlayerCharacter = Player.Character or Player.CharacterAdded:Wait()
		local PlayerHumanoidRootPart = PlayerCharacter.HumanoidRootPart
		
		PlayerMapSnapshot[Player] = (HumanoidRootPart.CFrame.Position - PlayerHumanoidRootPart.CFrame.Position).Magnitude
		table.insert(Distances, PlayerMapSnapshot[Player])
	end
	
	for Player, Distance in pairs(PlayerMapSnapshot) do
		-- Do not consider dead players
		local PlayerCharacter = Player.Character
		local PlayerHumanoid = PlayerCharacter.Humanoid
		
		if Distance == math.min(unpack(Distances)) and Distance <= ATTACKING_RANGE and PlayerHumanoid.Health > 0 then
			return Player
		end
	end
	
	return nil
end

local Pathfinding = {}
Pathfinding.__index = Pathfinding

function Pathfinding:Init()
	local Humanoid = self.Character.Humanoid
	local HumanoidRootPart = self.Character.HumanoidRootPart
	
	while true do
		RunService.Heartbeat:Wait()
		
		if Humanoid and Humanoid.Health > 0 then
			local NearestPlayer = GetNearestPlayer(self.Character)
			
			if NearestPlayer then
				local PlayerCharacter = NearestPlayer.Character
				local PlayerHumanoidRootPart = PlayerCharacter.HumanoidRootPart
				
				local MobPath = PathfindingService:CreatePath()
				
				local success, errormessage = pcall(function()
					MobPath:ComputeAsync(HumanoidRootPart.CFrame.Position, PlayerHumanoidRootPart.CFrame.Position)
				end)
				
				if success and MobPath.Status == Enum.PathStatus.Success then
					for _, Waypoint in pairs(MobPath:GetWaypoints()) do
						Humanoid:MoveTo(Waypoint.Position)
					end
				end
			end
		else
			break
		end
	end
end

function Pathfinding.new(Character)
	local self = setmetatable({}, Pathfinding)
	self.Character = Character
	
	self:Init()
	
	return self
end

return Pathfinding

I’ve just used OOP as I can separate 2 functionalities (damage and pathfinding) into 2 module scripts as to not clutter my code. But the way the code posted above works is:

  1. Get nearby player
  2. If successful, compute path to follow player
  3. Loop through the paths and follow the player

All of these steps are done in a while true do loop as seen in the statement. This works smoothly and perfectly without bugs. Since it’s a pathfinding mechanic you may as well take the code for your own work.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.