Need help with pathfinding enemy AI

Hello reader!

I’m trying to make a pathfinding AI, and so far I kinda got what I wanted, but the rig’s movement is very inconsistent, despite the path being found for a lack of better words. I want it to smoothly pathfind towards the player. If theres a better method than “Humanoid:MoveTo()” I would love to know.

The code:

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

local RootPart = script.Parent.HumanoidRootPart
local Humanoid = script.Parent.Humanoid

local Pathfinding = PathFindingService:CreatePath()



local function GetClosestPlayer()
	local RootPosition = RootPart.Position

	local distance = math.huge
	local closestPlayer = nil

	for _, v in pairs(PlayerService:GetChildren()) do
		local character = v.Character
		if character then
			local HRP = character:FindFirstChild("HumanoidRootPart")
			if HRP then
				local PlayerPos = HRP.Position
				local DistFromEntity = (RootPosition - PlayerPos).magnitude

				if DistFromEntity < distance then
					distance = DistFromEntity
					closestPlayer = v
				end
			end
		end

	end
	return closestPlayer, distance
end

RunService.Heartbeat:Connect(function()
	local player, distance = GetClosestPlayer()

	print(distance)
	
	if player and distance < 100 then
	local character = player.Character or player.CharacterAdded:Wait()	
	print(player.Name)
	Pathfinding:ComputeAsync(RootPart.Position, character:FindFirstChild("HumanoidRootPart").Position)
	local Points = Pathfinding:GetWaypoints()
	
	if Pathfinding.Status == Enum.PathStatus.Success then

		for _, v in pairs(Points) do
				local Part = Instance.new("Part")
			Part.Position = v.Position
			Part.Anchored = true
			Part.CanCollide = false
			Part.Size = Vector3.new(5,5,5)
			Part.Shape = Enum.PartType.Ball
			Part.Parent = game.Workspace
			Part.Color = Color3.fromRGB(170,0,170)
			task.wait(0.1)
			Part:Destroy()
		end

		for _, Point in pairs(Points) do
			Humanoid:MoveTo(Point.Position)

		end
		
	end
	
	end --the end for the first if statement
end)

The first part of the code is just to track the closest player. The second part is the pathfinding (it also has an extra part I added to visualise the pathfinding)

Video:

1 Like

The main problem is that the npc is walking towards only the last point in the path because you iterated through all the points and then instantly used MoveTo, so all the previous ones got overwritten (In this part of your code)

for _, Point in pairs(Points) do
	Humanoid:MoveTo(Point.Position)

end

The correct way is to generate the path one time, walk to the first point, wait until you get within a certain distance of that point, and then keep continuing to the next point. You also should not regenerate the path in the heartbeat loop, only when the target position changes or the path gets blocked

1 Like

Hey, from the looks of it, the other guy might be right, but I think you’re better off using a pathfinding module called NoobPath it makes pathfinding easier. Goodluck!

1 Like
for _, Point in pairs(Points) do
	Humanoid:MoveTo(Point.Position)

end

Humanoid:MoveTo doesnt block your script until they reach the point - instead, it says “hey go here” and immediately continues with the loop, without waiting for the npc to reach there. Try this:

for _, Point in pairs(Points) do
        local reached = false
        --we need to repeat because MoveToFInished fires even if its been 8 seconds and
       --it timeouts (imagine the npc gets stuck). Though in this case, you probably want to do more than simply retry
        repeat
	  Humanoid:MoveTo(Point.Position)
          reached = Humanoid.MoveToFInished:Wait() --this will block your script until the event fires
        until reached
end

Another thing is, if the player is moving away, the inital path you generated will slowly become a bad path, and you would need to recalculate it again, which makes NPC pathfinding modules a little bit hard to get started with.

1 Like