Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
1

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

Asked by 4 years ago

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
0
You could maybe get the waypoints “path:GetWaypoints()” and have the zombie re-evaluate at each waypoint ABK2017 406 — 4y

1 answer

Log in to vote
0
Answered by 4 years ago

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.

Ad

Answer this question