Need help with Pathfinding to player

Hi,
I have been trying to make pathfinding work on my enemy NPC for hours now (so that I can add more complex maps)

I came across a couple of issues, but I got it to work somewhat. Here is the current code on the pathfinding part. (sorry if it’s kind of messy I have been trying for a while) (the enemy runs off 1 script)

CollectionService:GetInstanceAddedSignal("Enemy"):Connect(function(enemyChar)

local humanoid = enemyChar:FindFirstChild("Enemy")
local torso = enemyChar:FindFirstChild("HumanoidRootPart")
local targetPlayer = enemyChar:FindFirstChild("targetPlayer")
wait()
torso:SetNetworkOwner(nil) 

  RunService.Heartbeat:Connect(function()
		if (enemyChar ~= nil) and (humanoid.Health > 0) and (ReplicatedStorage.playersLeft.Value > 0) and (Workspace:FindFirstChild(targetPlayer.Value) ~= nil) and (Workspace[targetPlayer.Value].Humanoid.Health > 0) and (Workspace[targetPlayer.Value].inGame.Value == true) then
		   local pathParameters = {
		    AgentHeight = 5,
			AgentRadius = 4,
			AgentCanJump = false
			}
			local path = game:GetService("PathfindingService"):CreatePath(pathParameters)
			path:ComputeAsync(torso.Position,game.Workspace[targetPlayer.Value].HumanoidRootPart.Position)
						
			local points = path:GetWaypoints()
			if path.Status == Enum.PathStatus.Success then
			   for i,v in pairs(points) do	
			       if((points[#points].Position) - (Workspace[targetPlayer.Value].HumanoidRootPart.Position)).magnitude > 10 then
				     break
			       end
					
			       if v.Action == Enum.PathWaypointAction.Jump then
				      humanoid.Jump = true
			       end
					
			       humanoid:MoveTo(v.Position)
							
			      local timeout = humanoid.MoveToFinished:Wait(0.5)
			      if not timeout then
				    humanoid.Jump = true
			        break
			      end
					
					
	          end
	    else
			humanoid:MoveTo(Workspace[targetPlayer.Value].Head.Position, Workspace[targetPlayer.Value].Head)
		end
		end
	end)
end)

I originally did not want to use RunService as I want to minimise the amounts of loops I use as it can get laggy however this was the only solution which worked the best so far. I have tried to enclose everything in a function and call it again and break it if the player moves too far from the last waypoint but for some reason, it results in the NPC not moving altogether after it detects that the player moved too far from the last waypoint.

This is the only issue that remains:after moving and kiting the enemy for a while, it sometimes just gets stuck on the side of the bridge. Sometimes it will target me again when I get close but most of the time it’s stuck there forever. I have no idea why it does this. There is no error in the output. It should at least MoveTo my head position according to the code.

stuck

Any ideas as to why it does this? Or can anyone improve on my pathfinding code, it’s definitely not great. I have been looking through the forums and videos on pathfinding and just trying it until it kind of works.

Thanks in advance.

optional issues to solve:

any ideas on how to make pathfinding work properly without RunService?

I tried adding a raycast check (so that it would pathfind only when necessary) but the movement was so choppy like the npc couldn’t decide whether it would pathfind or move. (sample code below) How do I make it not as choppy?

local ray = Ray.new(torso.Position, (Workspace[targetPlayer.Value].Head.Position - torso.Position).Unit * 400)
local hit,position = workspace:FindPartOnRayWithIgnoreList(ray,Workspace.currentMap:GetDescendants())
	if hit then
	   if hit.Parent:IsDescendantOf(Workspace[targetPlayer.Value]) and math.abs(hit.Position.Y - torso.Position.Y) < 3 then
		  humanoid:MoveTo(Workspace[targetPlayer.Value].Head.Position, Workspace[targetPlayer.Value].Head)
	   else
      --insert pathfinding code
       end
    end
2 Likes

update: I added this bit of code I found from here and removed humanoid.MoveToFinished:Wait()

repeat
	local distance = (v.Position - torso.Position).magnitude
	wait()
until distance <= 12

this allowed me to use the pathfinding code inside what is essentially a wait(0.6) loop (which was already inside my code to handle attacking and player targeting) which is good as instead of using 2 loops I am only using 1 now. Script activity is lowered as a result.

However, the issue of the enemy randomly getting stuck is still there. I have made it print the path.Status but whenever an enemy gets stuck it does not print anything / is still printing success

I haven’t really used pathfinding or anything like that so I’m not sure if there is a better way, but try so every x amount of seconds you raycast infront of hrp and check if it hits something (obviously make the ray small), if it does then make the npc jump?

You also could check if the distance is barely changing in the loop, and if it is then that means the npc is stuck and you should take action.

I think I’ve fixed the weird switching between raycast MoveTo and pathfinding mode. Pretty sure the mistake was that I added the whole currentMap folder to my ignorelist (which has all the remaining enemies inside it as well as the map itself). As such the ray ignored the map altogether and kept firing even though the enemy cant really ‘see’ the player.

Fixed it by making the ignorelist to get the tag “Enemy” instead of the whole folder. It seems to switch between the two modes better now.

On another note how would I check for distance change? I’m guessing I need to check distance over a period of time which kind of sounds complicated.

I found out that once the enemy breaks and stops moving it won’t damage you at all which is weird and leads me to believe that the while loop enclosing almost the whole script has stopped for some strange reason which should not happen unless it dies or no players are left. Below is a simplified of the while loop.

while (enemyChar ~= nil) and (humanoid.Health > 0) and (ReplicatedStorage.playersLeft.Value > 0) do
wait(0.6)
--target random player and check if player still alive, if not change target
--find nearest player and damage nearest player if close enough
--move to random player (pathfinding/raycast)

end
2 Likes