PathFindingService is not correctly leading the NPC

I’ve written a relatively simple pathfinding script that is supposed to make the NPC chase the player. I’ve done a ton of research on this and apparently pathfinding service is supposed to automatically guide the NPC. However, it wasn’t so I made a ledge detection system but even this doesn’t work. I would really like my NPCs to smartly navigate the maze of platforms to get to the player WITHOUT falling off. Does anyone know how I can achieve this?

Also I would like them to not kill themselves when a player jumps off a ledge to lead them to their death.

-- Services
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")

-- Variables
local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local root = npc:WaitForChild("HumanoidRootPart")
local chaseDistance = script:GetAttribute("ChaseDistance")
local stopDistance = script:GetAttribute("StopDistance")
local damage = script:GetAttribute("Damage")
local attackRange = script:GetAttribute("AttackRange")
local attackCooldown = script:GetAttribute("AttackCooldown")

local weapon = script.Parent.ClassicSword -- Change this to the weapon you want
local toolBindable = script.Parent.ToolBe
local lastAttack = tick()
root:SetNetworkOwner(nil)
local connection

-- Functions
local function ledgeCheck()
    local rayStart = root.Position + (root.CFrame.LookVector * 2)
    local rayEnd = rayStart - Vector3.new(0, 10, 0)
    local ray = Ray.new(rayStart, rayEnd - rayStart)
    local hit = workspace:FindPartOnRay(ray)

    return not hit
end

function findNearestPlayer()
    local playerList = game.Players:GetPlayers()

    local targetPlayer = nil
    local targetDistance = nil
    local targetDirection = nil
    local targetPosition = nil

    for _, player in pairs(playerList) do 
        if player.Character then
            local hum = player.Character:FindFirstChild("HumanoidRootPart")
            if hum then
                local distanceVector = player.Character.HumanoidRootPart.Position - root.Position
                if not targetPlayer then
                    targetPlayer = player
                    targetDistance = distanceVector.Magnitude
                    targetDirection = distanceVector.Unit
                    targetPosition = player.Character.HumanoidRootPart.Position
                elseif distanceVector.Magnitude < targetDistance then
                    targetPlayer = player
                    targetDistance = distanceVector.Magnitude
                    targetDirection = distanceVector.Unit
                    targetPosition = player.Character.HumanoidRootPart.Position
                end
            end
        end
    end

    return targetPlayer, targetDistance, targetDirection, targetPosition 
end

-- Events
connection = RunService.Heartbeat:Connect(function()
    local targetPlayer, targetDistance, targetDirection, targetPosition = findNearestPlayer()
    if targetPlayer then

        -- Check if target is within chase distance
        if targetDistance <= chaseDistance then
            humanoid:EquipTool(weapon)

            -- Check if target is within stop distance
            if targetDistance >= stopDistance then
                local path = PathfindingService:FindPathAsync(root.Position, targetPosition)
                if path.Status == Enum.PathStatus.Success then
                    local waypoints = path:GetWaypoints()

                    if humanoid.FloorMaterial ~= Enum.Material.Air and not ledgeCheck() then
                        for _, waypoint in ipairs(waypoints) do
                            local waypointPosition = waypoint.Position
                            local waypointAction = waypoint.Action
                            if waypointAction == Enum.PathWaypointAction.Jump then
                                humanoid.Jump = true
                            end
                            humanoid:MoveTo(waypointPosition)
                        end
                    end
                end
            else
                humanoid:MoveTo(root.Position)
            end

            -- Check if target is within attack range
            if targetDistance <= attackRange and tick() - lastAttack >= attackCooldown then
                -- animation here
                lastAttack = tick()
                local targetHumanoid = targetPlayer.Character:FindFirstChildOfClass("Humanoid")
                if targetHumanoid and targetHumanoid.Health > 0 then
                    toolBindable:Fire()
                    print("fired")
                end
            end
        else
            humanoid:UnequipTools()
        end
    else
        humanoid:MoveTo(root.Position)
    end
end)

humanoid.Died:Connect(function()
    if humanoid.Health == 0 then
        print(`Destroying {script.Parent.Name}...`)
        wait(1)
        connection:Disconnect()
        script.Parent:Destroy()
    end
end)
1 Like

I had the same problem once and you’re better off not using pathfindingservice. For simple things pathfindingservice is fine but more advanced stuff like this is where pathfindingservice becomes bad. You can however if you have the skills to do so make your own pathfinding module using A* Pathfinding.

1 Like

You are not waiting for the humanoid:MoveTo() to reach the waypoint, so it just keep calling humanoid:MoveTo() and eventually it ended up going to the last waypoint, which is where the player is at instead of following all the waypoints.

use humanoid:MoveToFinished:Wait() after you use humanoid:MoveTo()

everything including not following player jumping off should be fixed(cuz it can’t skywalk and therefore can’t find a path)

1 Like