I’m working on a pathfinding module that uses Roblox’s PathfindingService API. When the NPC detects an obstacle, I have it compute a path and follow it. This does work to some extent, but the NPC becomes very jittery. I have narrowed down the problem down to a ComputePath() function that the module has, and the way that I’m calling the function.
The GetSight() function just draws a ray from the NPC to the target to check if there is a direct path that can be more easily followed with the MoveTo() humanoid function.
function EntityController:ComputePath(ENTITYROOT,ENTITYHUM,TARGET)
local PATH = PATHSERVICE:CreatePath()
PATH:ComputeAsync(ENTITYROOT.Position,TARGET.Position)
local WAYPOINTS = PATH:GetWaypoints()
if PATH.Status == Enum.PathStatus.Success then
for i,POINT in ipairs(WAYPOINTS) do
if POINT.Action == Enum.PathWaypointAction.Jump then
ENTITYHUM.Jump = true
end
ENTITYHUM:MoveTo(POINT.Position)
local SUCCESS = ENTITYHUM.MoveToFinished:wait()
if not SUCCESS or GetSight(ENTITYROOT,TARGET) then break end
if i % 3 == 0 then -- for every 3rd waypoint, check again for new target
if FindTarget(ENTITYROOT) ~= TARGET then
break
end
end
end
end
end
Here’s a gif of the jittery movement. As you can see, it can follow an unobstructed path, but will become jittery when it tries to compute and follow a path using PathfindingService.
Looks like it’s probably related to your GetSight function here. I’m guessing that’s returning a boolean which is false if the NPC cannot see its target. This causes the path to be reassessed, which is unnecessary. In my experience with the path finding service it helps to ignore the first point or two in a path since they tend to be so close to the NPC and tend to cause this back and forth motion you’re seeing. Just a hunch though.
It’s not safe to draw a ray from your NPC toward its target and assume the path is clear. Unless the NPC and target are roughly the same height, at the same position on the Y axis and are both very small there could be many objects between the two points that won’t actually block the ray. You would probably need to adapt a Region3 to do this instead, but even then it wouldn’t be totally accurate. If you’re sending out multiple rays then this method might work? Still, I think there are enough edge cases, like huge gaps between the NPC and target that would be an issue. Of course if that doesn’t exist in your game then it isn’t.
Additionally there’s not too much reason to reinitialize the Path object like is being done in the first line of the function here local PATH = PATHSERVICE:CreatePath() really only needs to happen once.
After some testing I was able to streamline my code and just remove the whole GetSight() bit altogether. Unfortunately, after a little while, the NPC’s movement goes from smooth to jittery. This is what it looks like after a few seconds of pathfinding. Also, skipping the first waypoint works wonderfully, so thank you for that.
PATH:ComputeAsync(ENTITYROOT.Position,TARGET.Position)
local WAYPOINTS = PATH:GetWaypoints()
if PATH.Status == Enum.PathStatus.Success then
for i=2,#WAYPOINTS do
local POINT = WAYPOINTS[i]
ENTITYHUM:MoveTo(POINT.Position)
--[[ visualize waypoints
local part = Instance.new("Part")
part.Anchored = true
part.Size = Vector3.new(1,1,1)
part.CanCollide = false
part.Position = POINT.Position
part.Parent = workspace
]]--
if POINT.Action == Enum.PathWaypointAction.Jump then
ENTITYHUM.Jump = true
end
local REACHED = ENTITYHUM.MoveToFinished:wait()
if not REACHED then
print('failed')
break
end
end
end
I know this is a bit of a necro, but I’m having the same problem with the pathfinding behavior taking long pauses inbetween it’s movetowaypoint system.
I’ve tried other methods of moving on waypoints but I can’t seem to get rid of that factor.
local function moveToNextWaypoint()
print("Waypoint step")
if waypointIndex > #waypoints or yield then
--pathBlockedEvent:Disconnect()
else
MobAsset.Humanoid:MoveTo(waypoints[waypointIndex].Position)
waypointIndex = waypointIndex + 1
MobAsset.Humanoid.MoveToFinished:Wait() -- yields too long
moveToNextWaypoint()
end
end
I have older code from one of my older projects that has the same issue as well.
while wait() do
local head = findTarget()
if head then
aiPart = script.Parent.HumanoidRootPart.Position
local hit = rayPathfind(head)
--local ray = Ray.new(aiPart, (head.Position - aiPart).Unit * 200)
--local otherInfected = findOtherInfected()
--local hit,position = workspace:FindPartOnRayWithIgnoreList(ray,otherInfected)
if hit then
print(hit)
if hit:IsDescendantOf(head.Parent) then
mob:MoveTo(head.Position)
wait()
else
local path = pathServ:FindPathAsync(aiPart,head.Position)
if path.Status == Enum.PathStatus.Success then
points = path:GetWaypoints()
--[[for i,v in ipairs(points) do
local draw = Instance.new("Part",workspace)
draw.Anchored = true
draw.CanCollide = false
draw.CFrame = CFrame.new(v.Position)
draw.Size = Vector3.new(10,10,10)
end]]
for i,v in ipairs(points) do
mob:MoveTo(v.Position)
mob.MoveToFinished:wait()
if v.Action == Enum.PathWaypointAction.Jump then
mob.Jump = true -- I don't want the AI to jump, ever.
end
local hit2 = rayPathfind(head)
if hit2 and hit2:IsDescendantOf(head.Parent) then
break
elseif (points[#points].Position - head.Position).magnitude > 15 then
break
end
end
else
mob:MoveTo(head.Position) -- Idle animation?
end
end
end
end
I had the same issue when i was creating an npc. This can happens because as you can see in your gif when the character reaches waypoint it wait for the next waypoint to be generated and it takes some time for it that is why it pauses. To fix that issue you could delete the REACHED function or you could make so it would generate the path for the next waypoint as well.
For me deleting this ENTITYHUM.MoveToFinished:wait() worked but for you it may be different
I hope I helped