AI pathfinding stutters

Hello, I have been messing around with pathfinding and finding it quite fun. However, recently I noticed my script has been causing them to, what I believe, is finish each movement early, causing the animation to reset midway. Also, it seems to just stop sometimes. I have it programmed to stop, but I have removed it and it persists. I tested it on a fresh model and it still happens.

Script:

local pathfindingService = game:GetService(“PathfindingService”)

local body = script.Parent.Torso or script.Parent.HumanoidRootPart
local humanoid = script.Parent.Humanoid
local body = script.Parent:FindFirstChild(“HumanoidRootPart”) or script.Parent:FindFirstChild(“Torso”) --Works on R6 and R15
local destination = game.Workspace.EndGoal.Position
local timeToStop = 0
local timeToBeat = 0
local start = game.Workspace.Start
local player = game.Players.LocalPlayer

local path = pathfindingService:CreatePath()

while true do
–Teleport onto start
body.CFrame = start.CFrame

--Make the path
path:ComputeAsync(body.Position, destination)  --Goes body position(torso) to destination

local waypoints = path:GetWaypoints()
local startTime = os.date("*t" )
--Go to movepoint, wait to hit it, then go to the next, all ther way through
for k, waypoint in pairs(waypoints) do
	local startTime = os.date("*t")
	local stopMovement = 0
	stopMovement = math.random(1,20)
	
	if stopMovement == 10 then
		timeToStop = math.random(.5,2)
		wait(timeToStop)
		
		turnAroundNow = math.random(1,60)
		if turnAroundNow == 1 then	
			turnDistance = math.random(20,50)		
			for i = 1, turnDistance do

				body.CFrame = body.CFrame * CFrame.fromEulerAnglesXYZ(0, math.rad(5), 0)
				wait()
			end	
			wait(1)
			for i = 1, turnDistance do

				body.CFrame = body.CFrame * CFrame.fromEulerAnglesXYZ(0, math.rad(-5), 0)
				wait()
			end	
		end
	
	else humanoid:MoveTo(waypoint.Position)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		humanoid.MoveToFinished:Wait()
		wait()
	end
end


local finishTime = os.date("*t")
if (startTime.min ~= finishTime.min) then
	finishTime.sec = finishTime.sec + 60
end

startTime = startTime.sec
finishTime = finishTime.sec

local timeItTook = finishTime - startTime
print("It took the AI " .. timeItTook .. " seconds to reach the finish")
body.CFrame = start.CFrame

end

4 Likes

Also, I’m new to posting on the forum, so knowing why the code block formatting did not cover all the code would be a plus, if you don’t mind my asking.

Maybe you could provide an gyazo animation, but if not that’s ok.

--Make the path
path:ComputeAsync(body.Position, destination)  --Goes body position(torso) to destination

local waypoints = path:GetWaypoints()
local startTime = os.date("*t" )
--Go to movepoint, wait to hit it, then go to the next, all ther way through
for k, waypoint in pairs(waypoints) do
	local startTime = os.date("*t")
	local stopMovement = 0
	stopMovement = math.random(1,20)
	
	if stopMovement == 10 then
		timeToStop = math.random(.5,2)
		wait(timeToStop)
		
		turnAroundNow = math.random(1,60)
		if turnAroundNow == 1 then	
			turnDistance = math.random(20,50)		
			for i = 1, turnDistance do

				body.CFrame = body.CFrame * CFrame.fromEulerAnglesXYZ(0, math.rad(5), 0)
				wait()
			end	
			wait(1)
			for i = 1, turnDistance do

				body.CFrame = body.CFrame * CFrame.fromEulerAnglesXYZ(0, math.rad(-5), 0)
				wait()
			end	
		end
	
	else humanoid:MoveTo(waypoint.Position)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		--humanoid.MoveToFinished:Wait()
		local timeOut = 0
		local myRoot = humanoid.Parent.HumanoidRootPart
			--local part = createpart(waypoint.Position)
			repeat
				
			
			humanoid:MoveTo(waypoint.Position)
			wait(.05)
			timeOut = timeOut +.05
			until timeOut > 1 or (myRoot.Position - waypoint.Position).magnitude < 5
			--local timeOut = myHuman.MoveToFinished:Wait(1)
			if  timeOut > 1 then -- if not timeOut then
				-- whatever you want here
			end
			
		wait()
	end
	end


	local finishTime = os.date("*t")
	if (startTime.min ~= finishTime.min) then
	finishTime.sec = finishTime.sec + 60
	end

	startTime = startTime.sec
	finishTime = finishTime.sec

	local timeItTook = finishTime - startTime
	print("It took the AI " .. timeItTook .. " seconds to reach the finish")
	body.CFrame = start.CFrame

MoveToFinished:Wait() is the cause of the stuttering, so if you replace it with a loop that counts for a total of 1 second and calculates if the NPC is < 5 studs it will be more efficient. Note that this is not the best solution as the NPC will sometimes try moving towards a path that may get stuck in a corner of a brick or try walking into a wall if the map is small with lots of obstacles.

Well, if I do try to use that, the issue becomes I need it to be able to walk through mazes that are pretty tight, but I will try it anyways.

Here ya go:

It only does this in play mode btw, in run the animation kinda of breaks because the loop is acting weird is my guess, don’t know how to describe it, but I don’t really mind that as much as I do it basically stopping every second once it gets to the turns.

1 Like

Try setting the NetworkOwner on the NPC to the player while using MoveToFinished.

Check this:

humanoid:MoveTo(waypoint.Position)
wait(.05)
timeOut = timeOut +.05
until timeOut > 1 or (myRoot.Position - waypoint.Position).magnitude < 5

Are you telling it to wait in between each waypoint to add a timeout?

Try putting the timeout code outside of the loop moving the character.

Also I know a bug similar to this that affects my humanoids in studio when I use MoveTo(). It should work fine in a live game if your problem is related.

1 Like

Well the purpose of it is incase the NPC doesn’t reach the waypoint at a certain time, it will either recalculate the destination or whatever you insert into the the if-statement. I’ve used this for my AI with the pathfinding services and its worked for my intentions as obstacles are not too crazy, when my NPC gets stuck on a brick, the timeout will determine if it is stuck and decide based on the following functions. I’m aware its not the best technique and is sorta buggy. This method is preferably meant to keep the npc moving and not stutter.

if timeout > 1 then
– whatever you want
end

This seems to have worked, although I will either have to lighten up and widen the maze a bit, or mess with it myself, because at the current state he is hitting walls like a player would holding w, and only moving on because he was angled enough to eventually keep going. Here and there however, he just face plants and gets stuck.I think I could probably fix it by have him move back or something, but I should have what I need now, unless you have any tips for aiding the AI in fixing itself.

Now the AI hits walls but won’t face plant no matter how long I wait. I think I will just stick with this, as it is technically a working, not too weird looking AI.

Out of curiosity, did you try setting the network owner on the NPC with your original method?

I already got it pretty much working, so I’m going to stick with what Captain gave me. If I end up needing help again, I will look into it.

1 Like