NPC Stutter (Can't find a solution)

Hello, DevForum members.

I’ve been struggling with Roblox’s pathfinding system and haven’t been able to find a solution. I initially used .MoveToFinished, but then switched to Heartbeat from RunService, which significantly reduced the stuttering issue. However, I’m still experiencing a delay when the NPC walks. This delay seems to be tied to a specific function, Functions.Lunch(). Whenever this function runs and then stops, the stuttering occurs, lasting anywhere from 2 to 5 seconds before moving to the next pathfinding point. I would greatly appreciate any help or suggestions to resolve this issue.

Here’s the code:


Functions.Lunch = function(Student)
	wait(math.random(1,4))
	Chat(Student,MainModuleScript.Student_Messages.LunchTimeMessages[math.random(1,#MainModuleScript.Student_Messages.LunchTimeMessages)])

	if CheckingIfDetentionAndTakingAction(Student, "Lunch") then
		return
	end

	local LunchPoints = game:GetService("Workspace"):WaitForChild("LunchPoints")

	local function GoingOutside()
		local function MovingToPoints()
			local BreakTimePoints = game:GetService("Workspace"):WaitForChild("BreakTimePoints")
			local Base_Point = BreakTimePoints:GetChildren()[math.random(1, #BreakTimePoints:GetChildren())]
			local random_point = Vector3.new(math.random((Base_Point.Position.X - (Base_Point.Size.X / 2)),(Base_Point.Position.X + (Base_Point.Size.X / 2))),Student:WaitForChild("HumanoidRootPart").Position.Y, math.random((Base_Point.Position.Z - (Base_Point.Size.Z / 2)),(Base_Point.Position.Z + (Base_Point.Size.Z / 2))))
			local Path_Complete = MoveToPosition(random_point, Student, "Lunch")

			if Path_Complete then
				return
			end

			wait(math.random(0,2))
			MovingToPoints()
		end

		MovingToPoints()
	end

	local function GoingToTheLunchTables(Set)
		local ChosenSeat = nil
		print("GoingToTheLunchTables function has been ran")
		local function ChoosingRandomLunchTable()
			print("choosing seat")
			local LunchTables = game:GetService("Workspace"):WaitForChild("IndoorLunchTables")
			local RandomChosenLunchTable = LunchTables:GetChildren()[math.random(1,#LunchTables:GetChildren())]

			local FoundSeats = RandomChosenLunchTable:WaitForChild("Seats")
			ChosenSeat = FoundSeats:GetChildren()[math.random(1,#FoundSeats:GetChildren())]

			if ChosenSeat then
				if ChosenSeat:GetAttribute("Occupant") then
					-- Someone is already sitting down on this.
					ChosenSeat = nil
					ChoosingRandomLunchTable()
				else
					ChosenSeat:SetAttribute("Occupant",Student.Name)
				end
			end
		end

		ChoosingRandomLunchTable()

		local bool = MoveToPosition(ChosenSeat.Position,Student,"Lunch")

		if bool then
			return
		end

		ChosenSeat:Sit(Student:WaitForChild("Humanoid"))
		task.spawn(function()
			while task.wait(math.random(1,5)) do
				Chat(Student,MainModuleScript.Student_Messages.LunchTableMessages[math.random(1,#MainModuleScript.Student_Messages.LunchTableMessages)])
				
				if Functions.CheckPeroids("Lunch") then
					break
				end
			end
		end)
	end

	local Random_Val = math.random(1,20)
	local Chance = 17

	if Random_Val <= Chance then
		local ChosenPoint = LunchPoints:GetChildren()[math.random(1, #LunchPoints:GetChildren())]
		local CurrentPoint = ChosenPoint:GetAttribute("Points") + 1

		ChosenPoint:SetAttribute("Points", ChosenPoint:GetAttribute("Points") + 1)

		local function MovingInLine()
			if CurrentPoint == 1 then

			else
				CurrentPoint -= 1
			end

			local FinishedPath = MoveToPosition((ChosenPoint.CFrame * CFrame.new(0,0, (3*CurrentPoint))).Position, Student, "Lunch")

			if FinishedPath then
				return
			end
			
			if Functions.CheckPeroids("Lunch") then
				return
			end

			wait(2)

			if CurrentPoint > 1 then
				MovingInLine()
			else
				GoingOutside()
			end
		end

		MovingInLine()
	else
		local bool = GoingToTheLunchTables()

		if bool then
			GoingOutside()
		end
	end
end

Here’s the moving function:

module.MoveToPosition = function(Position:"Position", Student:"Student", CurrentLesson:"CurrentLesson", IfInDetention:"Detention",ContinueNoMatterWhat:"Continue path regardless of any params")
	local PathfindingService = game:GetService("PathfindingService")
	local Agent_Params = nil
	if Student.Parent.Name == "Teachers" then
		Agent_Params = {Costs = {["Staff Stairs"] = 1, Death = math.huge, Decoration = math.huge}, WaypointSpacing = 15, AgentCanJump = true, AgentRadius = 4,AgentHeight = 5.5}--]] AgentCanClimb = true}
	else 
		Agent_Params = {Costs = {["Staff Stairs"] = math.huge, Death = math.huge}, WaypointSpacing = 7, AgentCanJump = true,AgentRadius = 2.5, AgentHeight = 5.5}--[[AgentRadius = Student:GetExtentsSize().X / 2, AgentHeight = Student:GetExtentsSize().Y / 2--]]
	end
	local Path = PathfindingService:CreatePath(Agent_Params)

	Path:ComputeAsync(Student:WaitForChild("HumanoidRootPart").Position, Position)

	local Waypoints = Path:GetWaypoints()

	local Blocked = false
	local broke = false

	local notUsedEvent = nil
	local event_changed = nil


	for i, v in pairs(Waypoints) do
		local NextPath = false
		local CalculatedTime = (Student:WaitForChild("HumanoidRootPart").Position - v.Position).Magnitude
		notUsedEvent = Path.Blocked:Connect(function(CurrentIndex)
			if i <= CurrentIndex or i >= CurrentIndex then
				Blocked = true
			end
		end)

		event_changed = game:GetService("RunService").Heartbeat:Connect(function()
			if (Student:WaitForChild("HumanoidRootPart").Position - v.Position).Magnitude <= 3 then
				NextPath = true
			end
			task.spawn(function()
				while task.wait(1) do
					CalculatedTime -= 1
					if CalculatedTime <= 0 then
						NextPath = true
						break
					end
					
					if NextPath == true or broke == true or Blocked == true then
						NextPath = true
						break
					end
				end
			end)
		end)
		
		Student:WaitForChild("Humanoid"):MoveTo(v.Position)
		repeat task.wait() until NextPath or Blocked == true
		
		if not Student:GetAttribute("Stop") and ContinueNoMatterWhat then
			broke = true
			break
		end

		if Student:GetAttribute("Stop") and not ContinueNoMatterWhat then
			broke = true
			break
		end

		if Student:GetAttribute("Exclude") and not ContinueNoMatterWhat then
			broke = true
			break
		end

		if IfInDetention and Student:GetAttribute("Detention") and not ContinueNoMatterWhat then
			broke = true
			break
		end

		if CurrentLesson ~= nil and not ContinueNoMatterWhat then
			if module.CheckPeroids(CurrentLesson) then
				broke = true
			end
		end

		if broke == true then 
			break
		end

		if Blocked == true then
			break
		end

		if v.Action == Enum.PathWaypointAction.Jump then
			Student:WaitForChild("Humanoid"):ChangeState(Enum.HumanoidStateType.Jumping)
		end

		if Student:WaitForChild("Humanoid").Sit == true then
			Student:WaitForChild("Humanoid").Sit = false
			Student:WaitForChild("Humanoid").Jump = true
		end
		event_changed:Disconnect()
		notUsedEvent:Disconnect()
	end

	if broke == true then
		return true
	end
	
	if Blocked == true then
		module.MoveToPosition(Position, Student, CurrentLesson,IfInDetention,ContinueNoMatterWhat)
	end
end
3 Likes

message me via devforum and i’ll give you the solution.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.