My pathfinding zombie does not work

I want the zombie to chase the closest player using pathfinding

The issue is, the zombie does not move at all, and path status will always show Enum.PathStatus.NoPath for some reason

I haven’t try any solution so far, but changing local target = FindNearestPlayer() to local target = workspace.Path which is a part that makes the script work normally.

I am aware that the problem is the local function FindNearestPlayer() function but I don’t know how to fix it.

Here is the script:

local zombie = script.Parent
local human = zombie.Humanoid
local root = zombie.HumanoidRootPart
local dmgDebounce = false
local pathfinding = game:GetService("PathfindingService")	
local players = game:GetService("Players")
local detectRange = 200
local path = pathfinding:CreatePath()

function FindNearestPlayer()
	print("searching")
	for i, plr in pairs(players:GetPlayers()) do
		print(plr.Name)
		if plr.Character ~= nil and plr.Character:FindFirstChild("HumanoidRootPart") then
			if (plr.Character.HumanoidRootPart.Position - root.Position).Magnitude <= detectRange then
				return  plr.Character.HumanoidRootPart
			end	
		end
	end
	return nil
end


function chase(part)
	
	path:ComputeAsync(root.Position, part.Position)
	
	print(path.Status)
	
	if path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()

		for i, v in pairs (waypoints) do
			human:MoveTo(v.Position)
			print("chasing")
			human.MoveToFinished:Wait()
			if v.Action == Enum.PathWaypointAction.Jump then
				human.Jump = true
			end
			if (waypoints[#waypoints].Position - part.Position).magnitude > 15 then
				break
			end
		end
	end
end

zombie.Torso.Touched:Connect(function(part)
	local humanoid = part.Parent:FindFirstChild("Humanoid")
	if part and part.Parent.Name ~= "Zombie" and human.Health > 0 and humanoid and not dmgDebounce then
		dmgDebounce = true
		zombie.Head.Attack:Play()
		print("damaging")
		humanoid:TakeDamage(math.random(10,20))
		wait(5)
		dmgDebounce = false
	end
end)



while true do
	local target = FindNearestPlayer()
	if target ~= nil then
		chase(target)
		local p = Instance.new("SphereHandleAdornment")
		p.Color3 = Color3.fromRGB(255,0,0)
		p.Adornee =  workspace.Terrain
		p.CFrame = CFrame.new(target.Position)
		p.AlwaysOnTop = true
		p.Parent = workspace.Terrain
		game.Debris:AddItem(p,1)
	end
	wait()
end

Any possible solution would be appreciated.

2 Likes

Are there any errors in the output?

I did not find any error in the output at all.

Try replacing players:GetChildren() with players:GetPlayers()

I did, the zombie still does not move.

Does the script find and return a target Player? Try adding a print statement before the return plr.Character.HumanoidRootPart in the FindNearestPlayer function. If that works, then move to the next step and place print statements into the chase function at various points to confirm the NPC is doing it is expected to do.
Print is a great simple way to check that your script is doing what you expected, scatter it liberally throughout when testing.

2 Likes

You might want to check out this article on pathfinding in Roblox. It can help your script and also help with some of the things you didn’t consider, such as the path being blocked.

1 Like

Ive figured this out, after some coding :wink:

local myHuman = script.Parent:WaitForChild("Humanoid")
local myRoot = script.Parent:WaitForChild("HumanoidRootPart")
local head = script.Parent:WaitForChild("Head")
local lowerTorso = script.Parent:WaitForChild("LowerTorso")
 
local grab = script.Parent:WaitForChild("Grab")
local grabAnim = myHuman:LoadAnimation(grab)
grabAnim.Priority = Enum.AnimationPriority.Action
 
local grabSound = head:WaitForChild("Attack")
local screamSound = head:WaitForChild("Scream")
 
local clone = script.Parent:Clone()
 
function walkRandomly()
    local xRand = math.random(-50,50)
    local zRand = math.random(-50,50)
    local goal = myRoot.Position + Vector3.new(xRand,0,zRand)
    
    local path = game:GetService("PathfindingService"):CreatePath()
    path:ComputeAsync(myRoot.Position, goal)
    local waypoints = path:GetWaypoints()
    
    if path.Status == Enum.PathStatus.Success then
        for _, waypoint in ipairs(waypoints) do
            if waypoint.Action == Enum.PathWaypointAction.Jump then
                myHuman.Jump = true
            end
            myHuman:MoveTo(waypoint.Position)
            local timeOut = myHuman.MoveToFinished:Wait(1)
            if not timeOut then
                print("Got stuck")
                myHuman.Jump = true
                walkRandomly()
            end
        end
    else
        print("Path failed")
        wait(1)
        walkRandomly()
    end
end
 
function findPath(target)
    local path = game:GetService("PathfindingService"):CreatePath()
    path:ComputeAsync(myRoot.Position,target.Position)
    local waypoints = path:GetWaypoints()
    
    if path.Status == Enum.PathStatus.Success then
        for _, waypoint in ipairs(waypoints) do
            if waypoint.Action == Enum.PathWaypointAction.Jump then
                myHuman.Jump = true
            end
            myHuman:MoveTo(waypoint.Position)
            local timeOut = myHuman.MoveToFinished:Wait(1)
            if not timeOut then
                myHuman.Jump = true
                print("Path too long!")
                findPath(target)
                break
            end
            if checkSight(target) then
                repeat
                    print("Moving directly to the target")
                    myHuman:MoveTo(target.Position)
                    attack(target)
                    wait(0.1)
                    if target == nil then
                        break
                    elseif target.Parent == nil then
                        break
                    end
                until checkSight(target) == false or myHuman.Health < 1 or target.Parent.Humanoid.Health < 1
                break
            end
            if (myRoot.Position - waypoints[1].Position).magnitude > 20 then
                print("Target has moved, generating new path")
                findPath(target)
                break
            end
        end
    end
end
 
function checkSight(target)
    local ray = Ray.new(myRoot.Position, (target.Position - myRoot.Position).Unit * 40)
    local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent})
    if hit then
        if hit:IsDescendantOf(target.Parent) and math.abs(hit.Position.Y - myRoot.Position.Y) < 3 then
            print("I can see the target")
            return true
        end
    end
    return false
end
 
function findTarget()
    local dist = 50
    local target = nil
    local potentialTargets = {}
    local seeTargets = {}
    for i,v in ipairs(workspace:GetChildren()) do
        local human = v:FindFirstChild("Humanoid")
        local torso = v:FindFirstChild("Torso") or v:FindFirstChild("HumanoidRootPart")
        if human and torso and v.Name ~= script.Parent.Name then
            if (myRoot.Position - torso.Position).magnitude < dist and human.Health > 0 then
                table.insert(potentialTargets,torso)
            end
        end
    end
    if #potentialTargets > 0 then
        for i,v in ipairs(potentialTargets) do
            if checkSight(v) then
                table.insert(seeTargets, v)
            elseif #seeTargets == 0 and (myRoot.Position - v.Position).magnitude < dist then
                target = v
                dist = (myRoot.Position - v.Position).magnitude
            end
        end
    end
    if #seeTargets > 0 then
        dist = 200
        for i,v in ipairs(seeTargets) do
            if (myRoot.Position - v.Position).magnitude < dist then
                target = v
                dist = (myRoot.Position - v.Position).magnitude
            end
        end
    end
    if target then
        if math.random(20) == 1 then
            screamSound:Play()
        end
    end
    return target
end
 
function attack(target)
    if (myRoot.Position - target.Position).magnitude < 5 then
        grabAnim:Play()
        grabSound:Play()
        if target.Parent ~= nil then
            target.Parent.Humanoid:TakeDamage(25)
        end
        wait(0.4)
    end
end
 
function died()
    wait(5)
    clone.Parent = workspace
    game:GetService("Debris"):AddItem(script.Parent,0.1)
end
 
myHuman.Died:Connect(died)
 
lowerTorso.Touched:Connect(function(obj)
    if not obj.Parent:FindFirstChild("Humanoid") then
        myHuman.Jump = true
    end
end)
 
function main()
    local target = findTarget()
    if target then
        myHuman.WalkSpeed = 16
        findPath(target)
    else
        myHuman.WalkSpeed = 8
        walkRandomly()
    end
end
 
while wait(0.1) do
    if myHuman.Health < 1 then
        break
    end
    main()
end

hope it worked for you!
ai pathfinding

2 Likes

Apologize for the late reply.
I actually originally used this script for the pathfinding AI but somehow it works now.