Pathfinding is making NPC movement choppy

So I’ve been experimenting with pathfinding on NPCs recently, and it seems as if all the movement of my NPCs are becoming choppy as they keep on moving. https://gyazo.com/c5b1bacbd724dd600231aabce434ebba

I’ve tried viewing other posts on the forum, but none of them really helped with my specific problem. What I’m trying to achieve is obviously a smoother and cleaner look to the walk of the NPC.

Now, what I’m basically doing with the NPCs is that I’m spawning a recursive function that decides and moves the NPC to a specific location. Frankly, I don’t know if it’s a good idea to spawn recursive functions, so if anyone has alternatives, that would be great. Anyways, in the function, I would then start to decide the destination of the NPC, and the path it would take. The function is listed below.

function NPC:ExecuteBehavior()
if self.Type == (“Roamer”) then

  local EligibleSpawns = {}
  
  for i, Spawn in pairs(Workspace:WaitForChild("MovePoints"):GetChildren()) do
  	if (Spawn) ~= (self.Target) then
  		if (self.Target.Position.X) == (Spawn.Position.X) then
  			table.insert(EligibleSpawns, Spawn.Name)
  		end
  		if (self.Target.Position.Z) == (Spawn.Position.Z) then
  			table.insert(EligibleSpawns, Spawn.Name)
  		end
  	end
  end

local RandomSpawn = Workspace:WaitForChild(“MovePoints”):FindFirstChild(EligibleSpawns[math.random(1, > #EligibleSpawns)])

  self.Target = RandomSpawn 
  
  local Path = self:ComputePath(RandomSpawn)

  for i, Waypoint in ipairs(Path:GetWaypoints()) do
  	self.NPC.Humanoid:MoveTo(Waypoint.Position)
  	self.NPC.Humanoid.MoveToFinished:Wait()
  end

  self:ExecuteBehavior()

end
end

Also, here’s the script executing the functions.

for i = 1, 20 do
local b = a.new(“Roamer”)
spawn(function()
b:ExecuteBehavior()
end)
end

How would I be able to get rid of the choppiness of the NPCs? Also, any other feedback would be appreciated.

4 Likes

The NPCs most likely stutter because of the self.NPC.Humanoid.MoveToFinished:Wait() in your pathfinding waypoint table. You could try moving the NPCs into their waypoint just before they finish with their current one.

2 Likes

Alright, how exactly would I do this?

Would I just move the npc to their waypoint like .1 second before they reach it?

And if so, how exactly would I find the duration of how long it takes for the npc to get to their waypoint?

Sorry, I’m just a bit new to pathfinding, so any help would be appreciated.

While this would work in theory, I cannot guarantee 100% functionality based on dynamic changing environments. I do have a small catch in this code for that though:

local pointA = Vector3.new(0,0,0)
local pointB = Vector3.new(100,0,0)

local walkSpeed = 16
local distance = (pointB-pointA).magnitude

local timeTaken = distance/walkSpeed

wait(timeTaken-0.5)

if (pointB - AIPOSITION).magnitude < 0.5/walkSpeed + 0.1 then --check if the ai is close to the point, specifically, within half a stud
    --finished
else
    repeat -- continue waiting for the ai to get close
        wait(0.05)
    until (pointB - AIPOSITION).magnitude < 0.5/walkSpeed + 0.1
end
2 Likes

You can additionally establish a MoveToFinished listener event to yield and stop the loop early via a while true do loop that plays while the NPC is still moving.

I’m naturally a bad explainer, so I made an example script of this that could possibly help you.

-- example script moving Dummy to Dummy2

local PF = game:GetService("PathfindingService")

local path = PF:CreatePath()
path:ComputeAsync(script.Parent.Torso.Position, workspace.Dummy2.Torso.Position)

local wayPoints = path:GetWaypoints()

local RunService = game:GetService("RunService")

local DestPosition = Vector3.new(workspace.Dummy2.Torso.Position) -- Just as an example
local DistFactor = 3.3 -- Most regular-sized NPCs stop around 3 studs short from Torso - Waypoint Pos.
local Humanoid = script.Parent:WaitForChild("Humanoid")

for i = 1, #wayPoints do
    local wP = wayPoints[i]
    local wPDistance
    
    Humanoid:MoveTo(wP.Position)
    
    local MoveToFinished = false
    local ListenConn
    ListenConn = Humanoid.MoveToFinished:connect(function()
        ListenConn:Disconnect()
        MoveToFinished = true
    end)
    
    while MoveToFinished == false do
        wPDistance = (script.Parent.Torso.Position - wayPoints[i].Position).magnitude
        if wPDistance <= DistFactor then
            ListenConn:Disconnect()
            break
        end 
        RunService.Heartbeat:wait()
    end
end

This uses roughly the same premise as @marfit did, only without speed.

3 Likes