[Solved] Pathfinding Problem

So I come back to pathfinding a year later to see if it is any less horrible than it was when I first had to learn it and well I must say to my disappointment nothing has changed. Monsters are still completely unable to detect if your character or waypoint is at an elevation

local stranger = script.Parent
local humanoid = stranger.Humanoid
local PathfindingService = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")

stranger.PrimaryPart:SetNetworkOwner(nil)

local function canSeeTarget(target)
    local origin = stranger.HumanoidRootPart.Position
    local direction = (target.HumanoidRootPart.Position - stranger.HumanoidRootPart.Position).unit * 40
    local raycastParams = RaycastParams.new()
    raycastParams.FilterDescendantsInstances = {stranger}

    local result = workspace:Raycast(origin, direction, raycastParams)
    
    if result then
        local hit = result.Instance
        if hit and hit:IsDescendantOf(target) then
            return true
        end
    end
    
    return false
end

local function findTarget()
	local players = game.Players:GetPlayers()
	local maxDistance = 75
	local nearestTarget = nil

	for index, player in pairs(players) do
		if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
			local target = player.Character
			local distance = (stranger.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude

			if distance < maxDistance and canSeeTarget(target) then 
				nearestTarget = target
				maxDistance = distance
			end
		end
	end

	return nearestTarget
end

local function getPath(destination)
	local pathParams = {
		["AgentHeight"] = 6,
		["AgentRadius"] = 2,
		["AgentCanJump"] = true
	}
	local path = PathfindingService:CreatePath()
	path:ComputeAsync(stranger.HumanoidRootPart.Position, destination.Position, pathParams)
	return path
end

local function attack(target)
	local distance = (stranger.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude

	if distance > 3 then
                --refuses to detect when at elevation
		humanoid:MoveTo(target.HumanoidRootPart.Position)  
	else
		target.Humanoid.Health = 0
	end
end

local function walkTo(destination)
	local path = getPath(destination)
	if path.Status == Enum.PathStatus.Success then
		for index, waypoint in pairs(path:GetWaypoints()) do
			local target = findTarget()
			if target then
				print("Target Found", target.Name)
				attack(target)
				return
			else
				print("Moving to", waypoint.Position)
				humanoid:MoveTo(waypoint.Position)
				humanoid.MoveToFinished:Wait()   
			end    
		end
	else
		print("Failed to find a path to the destination")
	end
end

local function patrol()
	local waypoints = workspace.Waypoints:GetChildren()
	local randomNum = math.random(1, #waypoints)
	walkTo(waypoints[randomNum])
end

while true do
	patrol()
	RunService.Heartbeat:Wait()
end

Can someone please help me fix this? Thank you!

1 Like

Bumping this. Can anyone help?

Is pathfinding really this bad to a point that 56 people read my post and nobody can help :disappointed:

You need to be a little more descriptive. There isn’t enough information to give a lot of help.

Does this attack function get called?

1 Like

Yes it does. We can agree to disagree on the descriptive thing. It’s not hard to understand that the NPC can’t walk to waypoints or characters who are at an elevation on the Y axis…

So, :MoveTo is called then? The issue could be that you are calling :MoveTo too frequently and it is constantly starting and stopping in it’s tracks.

1 Like

As you can see in the picture, definitely not the problem here. You can clearly see they are indeed stuck, despite the fact there is a safe path to take towards me.

You want them to jump? That has nothing to do with PathfindingService, because you aren’t pathfinding to the player. If you were, however, you could add

if Waypoint.Action == Enum.PathWaypointAction.Jump then
    stranger.Humanoid.Jump = true
end

, to your path loop.

You need to use PathfindingService instead of :MoveTo, because otherwise you’re NPCs won’t know when they should jump. If you don’t want to use Pathfinding for attacking, then the best you can do is have them jump if the player is above them:

if target.HumanoidRootPart.Position.Y > stranger.HumanoidRootPart.Position.Y then
    stranger.Humanoid.Jump = true
end

That doesn’t work either and I know it doesn’t because I had copied one that has the waypoint action jump in it and people like myself noticed it doesn’t work and it wouldn’t even try to jump not like it has a jumpheight problem it just doesn’t take the correct path + it doesn’t jump so uhh yeah it definitely is both a pathfinding AND a moving problem with npc’s

I can definitely dig up the code for the one that pathfinds and I can send it here since you insist it works but i’m telling you it doesn’t I tried it already

Okay yeah so this one is supposed to at least make the waypoints work and it doesn’t. The NPC does not jump. It does not pathfind correctly and gets stuck the exact same way the other one does.

local PathfindingService = game:GetService("PathfindingService")

local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local hrp = npc:WaitForChild("HumanoidRootPart")
hrp:SetNetworkOwner(nil)

local walkAnim = humanoid.Animator:LoadAnimation(script.Walk)
local attackAnim = humanoid.Animator:LoadAnimation(script.Attack)

local pathParams = {
	AgentHeight = 5,
	AgentRadius = 3,
	AgentCanJump = true,
}

local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Blacklist
rayParams.FilterDescendantsInstances = {npc}

local lastPos
local animPlaying = false

local RANGE = 60
local DAMAGE = 30

local function canSeeTarget(target)
	local orgin = hrp.Position
	local direction = (target.HumanoidRootPart.Position - hrp.Position).Unit * RANGE
	local ray = workspace:Raycast(orgin, direction, rayParams)
	
	if ray and ray.Instance then
		if ray.Instance:IsDescendantOf(target) then
			return true
		else
			return false
		end
	else
		return false
	end
end

local function findTarget()
	local players = game.Players:GetPlayers()
	local maxDistance = RANGE
	local nearestTarget
	
	for i, player in pairs(players) do
		if player.Character then
			local target = player.Character
			local distance = (hrp.Position - target.HumanoidRootPart.Position).Magnitude
			
			if distance < maxDistance and canSeeTarget(target) then
				nearestTarget = target
				maxDistance = distance
			end
		end
	end
	
	return nearestTarget
end

local function getPath(destination)
	local path = PathfindingService:CreatePath(pathParams)
	
	path:ComputeAsync(hrp.Position, destination.Position)
	
	return path	
end

local function attack(target)
	local distance = (hrp.Position - target.HumanoidRootPart.Position).Magnitude
	local debounce = false
	
	if distance > 5 then
		humanoid:MoveTo(target.HumanoidRootPart.Position)
	else
		if debounce == false then
			debounce = true
			
			npc.Head.AttackSound:Play()
			attackAnim:Play()
			target.Humanoid.Health -= DAMAGE
			task.wait(0.5)
			debounce = false
		end
	end
end

local function walkTo(destination)
	local path = getPath(destination)
	
	if path.Status == Enum.PathStatus.Success then
		for i, waypoint in pairs(path:GetWaypoints()) do
			path.Blocked:Connect(function()
				path:Destroy()
			end)
			
			if animPlaying == false then
				walkAnim:Play()
				animPlaying = true
			end
			
			attackAnim:Stop()
			
			local target = findTarget()
			
			if target and target.Humanoid.Health > 0 then
				lastPos = target.HumanoidRootPart.Position
				attack(target)
				break
			else
				if waypoint.Action == Enum.PathWaypointAction.Jump then
					humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
				end
				
				if lastPos then
					humanoid:MoveTo(lastPos)
					humanoid.MoveToFinished:Wait()
					lastPos = nil
					break
				else
					humanoid:MoveTo(waypoint.Position)
					humanoid.MoveToFinished:Wait()
				end
			end
		end
	else
		return
	end
end

local function patrol()
	local waypoints = workspace.Waypoints:GetChildren()
	local randomNum = math.random(1, #waypoints)
	walkTo(waypoints[randomNum])
end

while task.wait(0.2) do
	patrol()
end

The code you showed does not ever try to jump in the attack function. It is not a problem with PathfindingService, because the attack function doesn’t use PathfindingService. You’ll need to use something like I showed if you are not going to use PathfindingService in your attack function (specifically the code I gave that compares the height of the Player and your NPC).

What don’t you understand? The attack function isn’t the problem here. I can stand completely away from it, let it never get called, and the part where it uses pathfindingservice does… not… work. In fact, I’d be wasting your time, my time, and everybody’s time to make it work when we can’t even get the waypoints part to work. Let’s fix that first, then it won’t be a waste of everyone’s time to fix the attack part.

There is no code to handle jumping in your path loop either, so even if PathfindingService tells you a Waypoint is a Jump waypoint, your code does not handle it. You can add a condition to your loop to fix that:

for _, waypoint in path:GetWaypoints() do
	local target = findTarget()
	if target then
		print("Target Found", target.Name)
		attack(target)
		return
	else
		print("Moving to", waypoint.Position)
		
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			print("Jumping!")
			humanoid.Jump = true
		end
		
		humanoid:MoveTo(waypoint.Position)
		humanoid.MoveToFinished:Wait()
	end    
end
2 Likes

Nice code. I’m sure it is an improvement, but the NPC remains stuck just like he was from the beginning

Just wondering bc I’m curious, Why don’t you use the WalkTo function that you made here :thinking: ?

The WalkTo function isn’t going to find targets. Also, the waypoints it does get called. So idk what you mean

Does it print Jumping! to the Output?

2 Likes

Yes and I saw it try to jump and it failed miserably :joy: it didn’t jump high enough. That’s completely besides the point though. You see, the monsters are being totally stupid and not taking the correct path to the waypoint. Seeing them jump high is the equivalent of them being really dumb exploiters which 100% has a negative effect on the overall fun of the game.

I was just wondering why you used a MoveTo(target) instead of a WalkTo(target) since WalkTo does call the getPath and use pathfinding while MoveTo does not ( I think, I’m not too well versed in pathfinding )

1 Like