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

Pathfinding makes NPC go back and forth instead of straight for the target, why?

Asked by 2 years ago

So I've asked this before, but after messing around a little bit I think I found why, but I don't know what to do about it, so here is the part of the script that seems to fail:

-- Chase

    local plr_HRP = plr_character.HumanoidRootPart

    local destination = plr_HRP.Position
    local beginning = HRP.Position

    local path = PathfindingS:CreatePath(PathInfo)
    path:ComputeAsync(beginning, destination)

    if path.Status == Enum.PathStatus.Success then
        local waypoints = path:GetWaypoints()

        for cry, waypoint in pairs(waypoints) do
            if waypoints.Action == Enum.PathWaypointAction.Jump then
                Hum.Jump = true
            end

            Hum:MoveTo(waypoint.Position)
            Hum.MoveToFinished:Wait()
        end

    else
        Hum:MoveTo(HRP.Position)
    end

So this is what it looks like when i say it glitches: (gif) https://gyazo.com/cfd6fd7e9b350809dd352f2ed6cac2e9

Now, this doesn't happen if i remove Hum.MoveToFinished:Wait()

But I mean, that is a necessary part of a chase script no? for the enemy to wait before going to the next waypoint

What could i do?

If for some reason you need to see the full script, here it is:

--- Services ---

local Plrs = game:GetService("Players")
local RunS = game:GetService("RunService")
local PathfindingS = game:GetService("PathfindingService")

--- Variables ---

local char = script.Parent.Parent
local Hum = char.Humanoid
local HRP = char.HumanoidRootPart

local CanJump = false

local FOV = 50
local CharSize = char:GetExtentsSize()

local PathInfo = {
    ModelRadius = (CharSize.X + CharSize.Z)/4,
    ModelHeight = CharSize.Y,
    ModelCanJump = CanJump
}

--- Initialization --- 

HRP:SetNetworkOwner(nil)

--- Functions ---

local function FindClosestPlr()
    local players = {}
    local ClosestDistance = math.huge
    local target

    for index, targetplr in pairs(Plrs:GetPlayers()) do
        local TargetChar = targetplr.Character

        if TargetChar == nil then
            continue
        end

        local distance = (TargetChar.HumanoidRootPart.Position - HRP.Position)
        if distance.Magnitude <= FOV then
            table.insert(players, {
                Magnitude = distance.Magnitude,
                Player = targetplr
            })
        end
    end

    for i, entry in pairs(players) do
        local magnitude = entry.Magnitude
        local player = entry.Player

        if magnitude < ClosestDistance then
            ClosestDistance = magnitude
            target = player
        end
    end

    return target
end


function Chase()
    local plr = FindClosestPlr()

    if plr == nil then
        return
    end

    if plr.Character.Humanoid.Health <= 0 then
        return
    end

    local plr_character = plr.Character or plr.CharacterAdded:Wait()

    local plr_humanoid = plr_character.Humanoid

    -- Chase

    local plr_HRP = plr_character.HumanoidRootPart

    local destination = plr_HRP.Position
    local beginning = HRP.Position

    local path = PathfindingS:CreatePath(PathInfo)
    path:ComputeAsync(beginning, destination)

    if path.Status == Enum.PathStatus.Success then
        local waypoints = path:GetWaypoints()

        for cry, waypoint in pairs(waypoints) do
            if waypoints.Action == Enum.PathWaypointAction.Jump then
                Hum.Jump = true
            end

            Hum:MoveTo(waypoint.Position)
            Hum.MoveToFinished:Wait()
        end

    else
        Hum:MoveTo(HRP.Position)
    end

end

--- Connections ---

RunS.Heartbeat:Connect(Chase)

1 answer

Log in to vote
1
Answered by 2 years ago

The problem is because you're using Run Service's heartbeat signal which isn't ideal as signals create new threads meaning it won't yield for it to complete, which means it will try to get to a new waypoint, but it is also trying to get to an old waypoint.

So instead of using heartbeat signal you can make the function call itself when it ends as well as before it returns (add a wait to prevent script from timing out) and add the chase function at the end of the script. Example:

function Chase()
    if plr == nil then
        wait(1)
        Chase()
        return
    end
    -- Code here
    Chase()
end

Chase()

Also, this will cause the NPC to stop which I don't know how fix. It will also yield the thread but to fix this you can just wrap it in coroutine.

Full script after fix (not wrapped in coroutine):

--- Services ---

local Plrs = game:GetService("Players")
local RunS = game:GetService("RunService")
local PathfindingS = game:GetService("PathfindingService")

--- Variables ---

local char = script.Parent.Parent
local Hum = char.Humanoid
local HRP = char.HumanoidRootPart

local CanJump = false

local FOV = 50
local CharSize = char:GetExtentsSize()

local PathInfo = {
    ModelRadius = (CharSize.X + CharSize.Z)/4,
    ModelHeight = CharSize.Y,
    ModelCanJump = CanJump
}

--- Initialization --- 

HRP:SetNetworkOwner(nil)

--- Functions ---

local function FindClosestPlr()
    local players = {}
    local ClosestDistance = math.huge
    local target

    for index, targetplr in pairs(Plrs:GetPlayers()) do
        local TargetChar = targetplr.Character

        if TargetChar == nil then
            continue
        end

        local distance = (TargetChar.HumanoidRootPart.Position - HRP.Position)
        if distance.Magnitude <= FOV then
            table.insert(players, {
                Magnitude = distance.Magnitude,
                Player = targetplr
            })
        end
    end

    for i, entry in pairs(players) do
        local magnitude = entry.Magnitude
        local player = entry.Player

        if magnitude < ClosestDistance then
            ClosestDistance = magnitude
            target = player
        end
    end

    return target
end


function Chase()
    local plr = FindClosestPlr()

    if plr == nil then
        wait(1)
        Chase()
        return
    end

    local plr_character = plr.Character or plr.CharacterAdded:Wait()

    local plr_humanoid = plr_character.Humanoid

    if plr_humanoid.Health <= 0 then
        wait(1)
        Chase()
        return
    end

    -- Chase

    local plr_HRP = plr_character.HumanoidRootPart

    local destination = plr_HRP.Position
    local beginning = HRP.Position

    local path = PathfindingS:CreatePath(PathInfo)
    path:ComputeAsync(beginning, destination)

    if path.Status == Enum.PathStatus.Success then
        local waypoints = path:GetWaypoints()

        for cry, waypoint in pairs(waypoints) do
            if waypoints.Action == Enum.PathWaypointAction.Jump then
                Hum.Jump = true
            end


            Hum:MoveTo(waypoint.Position)
            Hum.MoveToFinished:Wait()
        end

    else
        Hum:MoveTo(HRP.Position)
    end

    Chase()
end

--- Call Functions ---

Chase()
0
I've read about coroutines before but i've honestly got no clue what is it, and what is it used for, the zombie stopping is really sad since it happened to me before and i didn't know how to fix it either, nonetheless, you did answer WHY is it happening so thank you alot! SharkRayMaster 265 — 2y
Ad

Answer this question