Pathfinding not working but doesn't show errors

Hello, I’m RubyEpicFox. This is the first time I make a post btw.
So I have a little problem, I was writing a pathfinding module but found out that it didn’t work.
It does sort of work but instead of following the calculated path the AI walks to the point in a straight line which is not what I want.
I am unsure what causes this, I feel like it has something to do with the raycast that checks if the target is close enough and can be moved to in a straight line or something else.

You create a AI with MODULE.new(ai) where ai must be a model containing a humanoid and torso/root part.
height, radius and can jump are additional. (CanJump not functional yet.)
MODULE.remove(ai) removes the AI and cleans up everything.

To make the humanoid move, the path object contains a ObjectValue and a Vector3value.
What they are intended to do is, if any of those values are changed it should activate the pathfinding and make the humanoid calculate and walk a path to it’s goal.

local pfs = game:GetService("PathfindingService")

local mod = {}

mod.ai_list = {}




mod.new = function(ai, height, radius, canjump, refresh)
	local human = ai:FindFirstChild("Humanoid")
	local root = ai:FindFirstChild("HumanoidRootPart") or ai:FindFirstChild("Torso") or ai:FindFirstChild("LowerTorso")
	if human and root then
		
		mod.ai_list[ai] = {}
		mod.ai_list[ai].data = {
			current = 0;
			points = 0;
		}
		mod.ai_list[ai].waypoints = {}
		mod.ai_list[ai].running = false
		mod.ai_list[ai].enabled = true
		mod.ai_list[ai].connections = {}
		
		
		mod.ai_list[ai].path = pfs:CreatePath({
			AgentHeight = height or 5;
			AgentRadius = radius or 3;
		AgentCanJump = canjump or false})
		
		
		
		local path = mod.ai_list[ai].path
		path.Name = "AI"
		path.Parent = ai
		
		local target = Instance.new("ObjectValue")
		target.Name = "target"
		target.Parent = path
		
		local pos = Instance.new("Vector3Value")
		pos.Name = "position"
		pos.Parent = path
		
		
		
		
		mod.ai_list[ai].connections['targetupdated'] = target.Changed:Connect(function()
			if target.Value then
				local targ = target.Value
				
				if targ:IsA("BasePart") then
					
					path:ComputeAsync(root.Position, targ.Position)
					
					if path.Status == Enum.PathStatus.Success then
						mod.ai_list[ai].data.current = 1
						mod.ai_list[ai].waypoints = path:GetWaypoints()
						mod.ai_list[ai].data.points = #mod.ai_list[ai].waypoints
						
						mod.ai_list[ai].running = true
						human:MoveTo(mod.ai_list[ai].waypoints[mod.ai_list[ai].data.current].Position)
					else
						target.Value = nil
						warn("Failed to compute path.")
					end
					
				end
			end
		end)
		
		mod.ai_list[ai].connections['positionchanged'] = pos.Changed:Connect(function()
			local p = pos.Value
			if p and (p - root.Position).magnitude > 3 then
				path:ComputeAsync(root.Position, pos.Value)
					
				if path.Status == Enum.PathStatus.Success then
					mod.ai_list[ai].running = true
					
					mod.ai_list[ai].data.current = 1
					mod.ai_list[ai].waypoints = path:GetWaypoints()
					mod.ai_list[ai].data.points = #mod.ai_list[ai].waypoints
					
					human:MoveTo(mod.ai_list[ai].waypoints[mod.ai_list[ai].data.current].Position)
				else
					target.Value = nil
					warn("Failed to compute path.")
				end
				
			end
		end)
		
		
		mod.ai_list[ai].connections['movetofinished'] = human.MoveToFinished:Connect(function(succ)
			if succ and mod.ai_list[ai].data.points > 0 and mod.ai_list[ai].running then
				
				if mod.ai_list[ai].data.current >= mod.ai_list[ai].data.points then
					if target.Value ~= nil then
						if (target.Value.Position - root.Position).magnitude < 5 then
							mod.ai_list[ai].running = false
							human:MoveTo(target.Value.Position)
						end
					else
						if (pos.Value - root.Position).magnitude < 5 then
							mod.ai_list[ai].running = false
							human:MoveTo(pos.Value)
						end
					end
					
				end
				
				if target.Value ~= nil and mod.ai_list[ai].running then
					if (target.Value.Position - root.Position).magnitude < 20 then
						--Raycast.
						local ray = Ray.new(root.Position, CFrame.new(root.Position, target.Value.Position).LookVector)
						local hit = workspace:FindPartOnRay(ray, root.Parent, false, false)
						if hit ~= nil and (hit.Parent == target.Parent or hit.Parent.Parent == target.Parent) then
							
							mod.ai_list[ai].data.current = 0
							mod.ai_list[ai].data.points = 0
							mod.ai_list[ai].waypoints = {}
							
							human:moveTo(target.Value.Position)
							
						elseif mod.ai_list[ai].data.points > 0 then
							mod.ai_list[ai].data.current = math.max(mod.ai_list[ai].data.current + 1, mod.ai_list[ai].data.points)
							human:MoveTo(mod.ai_list[ai].waypoints[mod.ai_list[ai].data.current].Position)
						elseif mod.ai_list[ai].data.points < 1 then
							path:ComputeAsync(root, target.Value.Position)
							
							if path.Status == Enum.PathStatus.Success then
								
								mod.ai_list[ai].data.current = 1
								mod.ai_list[ai].waypoints = path:GetWaypoints()
								mod.ai_list[ai].data.points = #mod.ai_list[ai].waypoints
								
								mod.ai_list[ai].running = true
								human:MoveTo(mod.ai_list[ai].waypoints[mod.ai_list[ai].data.current].Position)
							else
								mod.ai_list[ai].running = false
							end
						end
						--End of raycast.
					else
						mod.ai_list[ai].data.current = math.max(mod.ai_list[ai].data.current + 1, mod.ai_list[ai].data.points)
						human:MoveTo(mod.ai_list[ai].waypoints[mod.ai_list[ai].data.current].Position)
					end
					
				elseif target.Value == nil and mod.ai_list[ai].running then
					if (pos.Value - root.Position).magnitude > 7 then
						mod.ai_list[ai].data.current = math.max(mod.ai_list[ai].data.current + 1, mod.ai_list[ai].data.points)
						human:MoveTo(mod.ai_list[ai].waypoints[mod.ai_list[ai].data.current].Position)
					else
						human:MoveTo(pos.Value)
					end
				end
			else
				if target.Value ~= nil or (pos.Value - root.Position).magnitude > 5 then
					human:MoveTo(mod.ai_list[ai].waypoints[mod.ai_list[ai].data.current].Position)
				else
					mod.ai_list[ai].running = false
					human:MoveTo(root.Position)
				end
			end
		end)
		
		
		mod.ai_list[ai].connections['pathblocked'] = path.Blocked:Connect(function(index)
			print("Path blocked.")
			if mod.ai_list[ai].data.points > 0 and index > mod.ai_list[ai].data.current then
				local position = Vector3.new(0,0,0)
				
				if target.Value ~= nil then
					position = target.Value.Position
				else
					position = pos.Value
				end
				
				path:ComputeAsync(root, position)
				
				if path.Status == Enum.PathStatus.Success then
					print("Successfully recalculated path.")
					mod.ai_list[ai].data.current = 1
					mod.ai_list[ai].waypoints = path:GetWaypoints()
					mod.ai_list[ai].data.points = #mod.ai_list[ai].waypoints
					
					mod.ai_list[ai].running = true
					human:MoveTo(mod.ai_list[ai].waypoints[mod.ai_list[ai].data.current].Position)
				else
					print("Failed to recalculate path.")
					mod.ai_list[ai].running = false
					
					mod.ai_list[ai].data.current = 0
					mod.ai_list[ai].data.points = 0
					mod.ai_list[ai].waypoints = {}
					
					human:moveTo(target.Value.Position)
				end
			end
		end)
		
		script.AIs.Value = script.AIs.Value + 1
	end
end










mod.remove = function(ai)
	if mod.ai_list[ai] == nil then print("AI does not exist.") return end
	
	for k, v in ipairs(mod.ai_list) do
		if mod.ai_list[k] == mod.ai_list[ai] then
			for _, connection in ipairs(mod.ai_list[ai].connections) do
				connection:Disconnect()
			end
			
			mod.ai_list[ai].path:Destroy()
			mod.ai_list[ai].path = nil
			
			table.remove(mod.ai_list, k)
			print("AI succesfully removed.")
			script.AIs.Value = script.AIs.Value - 1
		end
	end
	
end




return mod

So this is my whole module, it seems perfectly fine, to me atleast, I am unsure what causes my AI to not do what I wanted it to do.
The module is made for regular Roblox humanoids (not custom).
This is actually the second time I ever write a pathfinding module for NPCs.
I wrote one before that did work however… It was poorly optimized, used a lot of coroutines, etc.
Now I want to make a better optimalized one by using events and all instead of coroutines that check if the target is close enough/in a straight line, etc ever x times per second.

If anyone can help me it would be very appreciated, please tell me what I did wrong and/or what I could do better.

It’s a global pathfinding module btw, it’s not for a specific game or project, it’s made to be reused/shared.

Also note that it’s the first post I ever make, might still have to figure out how things work here.

3 Likes

Can you post the script? I can’t see the script.

1 Like