NPC is slow because of MoveToFinished pausing the script

Hey there, I am making an enemy NPC, and it’s working but the only problem is that the script is getting paused because of the MoveToFinished function, and if I remove it then the NPC won’t follow the paths correctly. Help is appreciated :slight_smile: Here’s my code:

local noob = script.Parent
local human = script.Parent:WaitForChild("Humanoid")
local rootpart = script.Parent:WaitForChild("Torso")


local pathfindingservice = game:GetService("PathfindingService")

local paths = game.Workspace:WaitForChild("Paths"):GetChildren()

local dist = 200

local function returnWaypointsAndCreatePath(startPoint, endPoint)
    local path = pathfindingservice:CreatePath()

    path:ComputeAsync(startPoint, endPoint);

    return path:GetWaypoints()
end
local function setNetworkOwner(npc)
    for i, v in pairs(npc:GetChildren()) do
        if v:IsA("BasePart") then
            v:SetNetworkOwner(nil)
        end
    end
end

local function findTarget()
    for index, target in pairs(workspace:GetChildren()) do
        if target:IsA("Model") and target:FindFirstChild("Humanoid") and target.Name ~= noob.Name and (target.PrimaryPart.Position - rootpart.Position).Magnitude <= dist then
            dist = (target.PrimaryPart.Position - rootpart.Position).Magnitude
            return target.PrimaryPart
        else
            dist = 200;
        end
    end
    return "random", Vector3.new(math.random(-50, 50),0, math.random(-50, 50))
end

local function followPath(startPoint, endPoint)
    local waypoints = returnWaypointsAndCreatePath(startPoint, endPoint)

    for index, point in pairs(waypoints) do
        if point.Action == Enum.PathWaypointAction.Jump then
            human.Jump = true
        end
        human:MoveTo(point.Position);
        local reached =  human.MoveToFinished:Wait(0.01)
    end  
end

setNetworkOwner(noob)

while wait() do
    local random = Random.new()

    local target, vector = findTarget()

    if target ~= "random" and vector == nil then
        followPath(rootpart.Position, target.Position)
    else
        followPath(rootpart.Position, rootpart.Position+vector);
    end
end

Anyone here gonna help me? if so then pls

Please help anyone. I need to be quick

I’ve run into this issue a few times. Try using magnitude to determine if the NPC reached the position. I find that a magnitude less than or equal to 5 works pretty well.

local RunService = game:GetService([[RunService]])

--Put this where you’re waiting for MoveToFinished():

repeat RunService.Heartbeat:Wait() until (HumanoidRootPart.Position - TargetPosition).magnitude <=5

--Continue your code

But how would I stop the code from being paused?

If you don’t yield at all your NPC will immediately go to the last point in your path.

Yeah I know that, but should I make my own MoveToFinished function and remove the actual MoveToFinished?

For slower yields times yes. Try using magnitude to determine where the NPC is instead of MoveToFinished()

Yeah just did that, should it be like this?

local function moveToFinished(humanoidRoot, targetPosition)
    repeat wait() until (humanoidRoot.Position - targetPosition).magnitude <= 5
end
1 Like

Exactly! I do recommend using RunService instead of wait() here because the latter might add unexpected delay to your NPC’s wait time.

Alright, let me add RunService wait here, thank you mate! :slight_smile:

1 Like

Here’s another problem, If I am near the NPC, then it won’t go to me, expect it would go to my old position and then come to me.

Can you please share your code now that you’re using magnitude?

Yeah, of course. Here:

local noob = script.Parent
local human = script.Parent:WaitForChild("Humanoid")
local rootpart = script.Parent:WaitForChild("Torso")

local runservice = game:GetService("RunService")

local pathfindingservice = game:GetService("PathfindingService")

local paths = game.Workspace:WaitForChild("Paths"):GetChildren()

local dist = 200

local function moveToFinished(humanoidRoot, targetPosition)
    repeat runservice.Heartbeat:Wait() until (humanoidRoot.Position - targetPosition).magnitude <= 10
end

local function returnWaypointsAndCreatePath(startPoint, endPoint)
    local path = pathfindingservice:CreatePath()

    path:ComputeAsync(startPoint, endPoint);

    return path:GetWaypoints()
end
local function setNetworkOwner(npc)
    for i, v in pairs(npc:GetChildren()) do
        if v:IsA("BasePart") then
            v:SetNetworkOwner(nil)
        end
    end
end

local function findTarget()
    for index, target in pairs(workspace:GetChildren()) do
        if target:IsA("Model") and target:FindFirstChild("Humanoid") and target.Name ~= noob.Name and (target.PrimaryPart.Position - rootpart.Position).Magnitude <= dist then
            dist = (target.PrimaryPart.Position - rootpart.Position).Magnitude
            return target.PrimaryPart
        else
            dist = 200;
        end
    end
    return "random", Vector3.new(math.random(-50, 50),0, math.random(-50, 50))
end

local function followPath(startPoint, endPoint)
    local waypoints = returnWaypointsAndCreatePath(startPoint, endPoint)

    for index, point in pairs(waypoints) do
        if point.Action == Enum.PathWaypointAction.Jump then
            human.Jump = true
        end
        human:MoveTo(point.Position);
        moveToFinished(rootpart, waypoints[index].Position)
    end  
end

setNetworkOwner(noob)

while wait() do
    local random = Random.new()

    local target, vector = findTarget()

    if target ~= "random" and vector == nil then
        followPath(rootpart.Position, target.Position)
    else
        followPath(rootpart.Position, rootpart.Position+vector);
    end
end

Edit: Scratch what I wrote for your moveToFinished() function. That was wrong. We need a new function to get the distance because moveToFinished() waits for the distance to be less than 10 to work. My mistake.

So the problem here seems to be that your NPC is following the old path. You will need to recalculate the path the NPC follows often if it’s chasing a player because the target position will always be moving. I recommend breaking the for loop if the player’s distance is too far from the original target position.

Luckily, the closer you are to your target position, the faster pathfinding calculations get. So we can avoid lag if we keep that in mind.

Here’s an example of what I mean:

local function getDistance(humanoidRoot, targetPosition)
   return (humanoidRoot.Position - targetPosition).magnitude
end

for index, point in pairs(waypoints) do
    if point.Action == Enum.PathWaypointAction.Jump then
        human.Jump = true
    end
    human:MoveTo(point.Position);
    local distance = getDistance(rootpart, waypoints[index].Position)
    if distance >= 10 then --If the player's distance is greater than 10, break the for loop so we can recalculate a path.
      break
    end
   --Wait for them to reach the position if the player is still close:
    moveToFinished(rootpart, waypoints[index].Position)
    end
2 Likes

Yeah. Let me try this real quick. If this works, then amazing.

I made a mistake. I edited the code some.

1 Like

Yeah. Seems to work now. Thank you so much! :love_you_gesture:

1 Like