Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
COMMUNITY QUESTION

How to I make NPCs update themselves while pathfinding their way to the nearest player?

I am working on a zombie pathfinding script that's meant to update itself to move to the nearest player. The path works great, but the zombies wait until the end of the path to update, instead of after every 2 seconds. I tried using bools and value.Changed events to alert the function when it should stop its path and update itself, but it doesn't seem to work. What do I do?

local zombie = script.Parent
local hrp = zombie.PrimaryPart
local hum = zombie.Humanoid
local values = zombie.Values
local mode = zombie.ZombieMode
local plrFolder = workspace:WaitForChild("InGamePlayers")

local foundPlr = false
local nearestPlr = nil
local nearestDist = nil
local pathActive = false
local cutPath = false

function findNearestPlayer()
	for i,v in pairs(workspace:GetChildren()) do -- searching for players
		if game.Players:FindFirstChild(v.Name) and v:FindFirstChild("HumanoidRootPart") ~= nil then
			local dist = (v.PrimaryPart.Position - zombie.PrimaryPart.Position).Magnitude
			if nearestDist == nil or nearestDist > dist and v.Humanoid.Health > 0 then
				nearestDist = dist
				nearestPlr = v
			end
		end
	end	
	if nearestPlr then
		followPath(nearestPlr)
	end
end

function followPath(target)
		
	zombie.Paths:ClearAllChildren()
	
	local targetHrp = target:FindFirstChild("HumanoidRootPart")
	
	local path = game:GetService("PathfindingService"):FindPathAsync(hrp.Position,targetHrp.Position)
	local points = path:GetWaypoints()
	
	local partColor = Color3.fromHSV(math.random(),1,1)
	
	for i,v in pairs(points) do
		local p = Instance.new("Part")
		p.Parent = zombie.Paths
		p.Anchored = true
		p.CanCollide = false
		p.Shape = Enum.PartType.Ball
		p.TopSurface = Enum.SurfaceType.Smooth
		p.CFrame = CFrame.new(v.Position)
		p.Size = Vector3.new(1,1,1)
		p.Color = partColor
	end
	
	if path.Status == Enum.PathStatus.NoPath then 
		print("there's no path! oh no")
		return nil 
	end
	
	if path.Status == Enum.PathStatus.Success then
		for i,v in pairs(points) do
			if cutPath then
				print("new path was called. Destroying current path.")
				cutPath = false
				return nil
			end
			if targetHrp == nil then print("no primary part") return nil end

			hum:MoveTo(v.Position)
			hum.MoveToFinished:wait()
			if v.Action == Enum.PathWaypointAction.Jump then
				hum.Jump = true
			end
		end
	end

end


if mode.Value == "Default" then
	while wait(3) do
		cutPath = true
		findNearestPlayer()
	end
end

1 comment

0

You could maybe get the waypoints “path:GetWaypoints()” and have the zombie re-evaluate at each waypoint

1 answer

You would have to put a break in the loop that controls the movement of a humanoid. Putting a break in the loop allows for it to generate a new path. In this example, the path recalcs every other point it walks to.

local zombie = script.Parent
local hrp = zombie.PrimaryPart
local hum = zombie.Humanoid
local values = zombie.Values
local mode = zombie.ZombieMode
local plrFolder = workspace:WaitForChild("InGamePlayers")

local foundPlr = false
local nearestPlr = nil
local dist = nil
local pathActive = false
local cutPath = false

function findNearestPlayer()
    dist = 9e9
    nearestPlr = nil
    for k, plr in pairs(game:GetService("Players"):GetPlayers()) do
        if nil ~= plr.Character then
            local human = plr.Character:FindFirstChildWhichIsA("Humanoid", true)
            if nil ~= human then
                if human.Health > 0 then
                    local ndist = plr:DistanceFromCharacter(zombie.PrimaryPart.Position)
                    if dist > ndist then
                        dist, nearestPlr = ndist, plr
                    end
                end
            end
        end
        if nil ~= plr then
            if nil ~= plr.Character then
                followPath(plr.Character)
            end
        end
    end
end

function followPath(target)
    zombie.Paths:ClearAllChildren()
    local targetHrp = target:FindFirstChild("HumanoidRootPart")
    local path = game:GetService("PathfindingService"):FindPathAsync(hrp.Position, targetHrp.Position)
    local points = path:GetWaypoints()
    local partColor = Color3.fromHSV(math.random(), 1, 1)
    for i,v in pairs(points) do
        local p = Instance.new("Part")
        p.Parent = zombie.Paths
        p.Anchored = true
        p.CanCollide = false
        p.Shape = Enum.PartType.Ball
        p.TopSurface = Enum.SurfaceType.Smooth
        p.CFrame = CFrame.new(v.Position)
        p.Size = Vector3.new(1,1,1)
        p.Color = partColor
    end
    if path.Status == Enum.PathStatus.NoPath then
        return print("there's no path! oh no")
    end
    if path.Status == Enum.PathStatus.Success then
        for i,v in pairs(points) do
            if cutPath then
                cutPath = false
                return print("new path was called. Destroying current path.")
            end
            if targetHrp == nil then print("no primary part") return nil end
            hum:MoveTo(v.Position)
            hum.MoveToFinished:wait()
            if v.Action == Enum.PathWaypointAction.Jump then
                hum.Jump = true
            else
                hum.Jump = false
            end
            if i > 1 then
                break -- Add a break to recalc path
            end
        end
    end
end

if mode.Value == "Default" then
    while wait(3) do
        cutPath = true
        findNearestPlayer()
    end
end

Note:You may want to change your while loop to a shorter duration.

This discussion is preserved as it was. New answers, comments, and votes are closed.