NPC Stuttering Issue

Okay, so I’ve recently been struggling with the following problems trying to make a proper NPC chasing system:

  • The NPC starts stuttering once I keep walking as it recalculates the path every time I change my position further than a few studs.
  • It starts teleporting randomly while chasing me without creating a path which I could not really find out why still appears to happen.
External Media

The following is the AI script:

local players = game:GetService("Players")
local teams = game:GetService("Teams")
local SSS = game:GetService("ServerScriptService")
local module = require(script.Parent)
local globalFunctions = require(SSS.globalFunctions)
local simplePath = require(game:GetService("ServerStorage").simplePath)
local SCP = script.Parent.Parent
local pathfinding = game:GetService("PathfindingService")

local path = pathfinding:CreatePath({
	AgentRadius = 3.5,
	AgentHeight = 6,
	AgentCanJump = false,
})

for i, part in pairs(script.Parent:GetChildren()) do
	if part:IsA("BasePart") then 
		part:SetNetworkOwner(nil)
	end
end

while task.wait(1) do

if(module.settings.on == false) then continue end

local closestChar = module.findNearestPathable()

if(not closestChar) then 
	warn("Could not find closestChar")
	continue 
end

local previousPosition = nil
local isThereACloseChar = module.findNearestUsingRay(closestChar)

local function shouldWalkInsteadOfPathing(destination, character)
	if(not destination) or (typeof(destination) ~= "Vector3") then
		return false
	end
	
	local distance = (SCP.HumanoidRootPart.Position-destination).Magnitude
	local YDifference = math.abs(destination.Y-SCP.HumanoidRootPart.Position.Y)
	
	local raycastParameters = RaycastParams.new()
	raycastParameters.FilterType = Enum.RaycastFilterType.Exclude
	raycastParameters.FilterDescendantsInstances = {SCP, character}
	local startPosition = SCP.HumanoidRootPart.Position
	local destinationPosition = (destination-startPosition).Unit * distance
	local raycastHitStatus = module.raycastHitCheck(startPosition, destination, distance, raycastParameters)
	print("raycastHitStatus:")
	print(raycastHitStatus)
	--print("Raycast:")
	--print(raycastHitStatus)
	
	local status = (distance < module.settings.minimumDistanceToWalkDirectly) and (YDifference < module.settings.maximumYDifferenceToWalkDirectly) and raycastHitStatus == nil
	
--	print("status: "..tostring(status))
	return status
end

local function reCalculatePath(destination)
	path:ComputeAsync(SCP.HumanoidRootPart.Position, destination)
	print("Computated path")
	if(path.Status == Enum.PathStatus.NoPath) then
		return warn("Could not generate the path!")
	end
	
	--print("Generated the path successfully.")
end

local previousPosition = nil

repeat 
		local closestCharUpdate = workspace:FindFirstChild(closestChar.Name)
		local hasTurnedIntoSCP 
		
		if(not closestCharUpdate) or (closestCharUpdate ~= closestChar) then
			break
		end
		
		local currentDistance = (SCP.HumanoidRootPart.Position-closestCharUpdate.HumanoidRootPart.Position).Magnitude
		local shouldWalkDirectly = shouldWalkInsteadOfPathing(closestCharUpdate.HumanoidRootPart.Position)
		isThereACloseChar = module.findNearestUsingRay(closestChar, currentDistance)
		
		local newPosition = closestCharUpdate.HumanoidRootPart.Position
		
		if shouldWalkDirectly then
			
			if(previousPosition) and (previousPosition-newPosition).Magnitude < 2 then
				task.wait(0.08)
				continue
			end
			
			task.wait(0.08)
			print("The SCP will just walk instead of generating a path.")
			SCP.Humanoid.WalkSpeed = 16
			SCP.Humanoid:MoveTo(closestCharUpdate.HumanoidRootPart.Position, closestCharUpdate.HumanoidRootPart)
			previousPosition = newPosition
			continue
		end
		
		reCalculatePath(closestCharUpdate.HumanoidRootPart.Position)
		
		if(path.Status == Enum.PathStatus.NoPath) then
			continue
		end
		
		local waypoints = path:GetWaypoints()
		local nextWaypoint = 2
		
		if(#waypoints) < 2 then
			break
		end
		
		print("Waypoint size: "..tostring(#waypoints))
		repeat
			local nextWaypointToWalk = waypoints[nextWaypoint]
			SCP.Humanoid.WalkSpeed = 16
			SCP.Humanoid:MoveTo(nextWaypointToWalk.Position)
			print("Moving to "..tostring(nextWaypointToWalk.Position))
			SCP.Humanoid.MoveToFinished:Wait()
			nextWaypoint += 1
		until (not closestChar) or shouldWalkInsteadOfPathing(closestCharUpdate.HumanoidRootPart.Position) or nextWaypoint == #waypoints+1 or (waypoints[#waypoints].Position-closestCharUpdate.HumanoidRootPart.Position).Magnitude > 3
	
until (not closestChar) or isThereACloseChar --(not module.isLooking(closestChar.Head, noob.Head.Position))

end

I’m on mobile so I can’t really debug your code but I have a post about something similar that might work for you

Thanks, I’ll try it shortly. (Trying to exceed the limit)

Hello, the script you’ve sent seems to have a long yield when it comes to walk between waypoints so I can not really benefit from it.

A reply just to bump the topic.

Three problems I see and that almost always cause problems (very common on here):
Nested for loops, many of them, make local functions seperate the code makes it so much easier to read, not only that the engine computes it differnetly… You just have one huge repeat until. Not only that, please understand what you are doing to the poor npc lol. Every time repeat is called, the server is running a calculation, THEN giving it the moveto command. This happens every single time the player moves, that means every direction, every stop, start, what direction, etc. We need to limit the calculations. I could go more into it but here is a snipit example:

local pathfindingService = game:GetService("PathfindingService")
local path = pathfindingService:CreatePath()
local cooldown = 0.5 -- Cooldown in seconds

local function followPlayer(closestChar)
    local lastWaypoint = nil
    local nextRepathTime = 0

    while task.wait() do 
       if tick() > nextRepathTime then
     -- random stuff code logic blah blah blah
       end
     --it is so important to declare the variable AFTER the end of your logic
     nextRepathTime = tick() + cooldown
    end
end

I hope this helps bring the point across. Also use movetofinished:wait() somewhere to allow the NPC to move during the function.

Use a module like SimplePath.
I used to face the same stuttering issue and I was able to fix it with this formula.
Destination + (RootPart.AssemblyLinearVelocity)/Divisor, Divisor could most optimally be ~3-4.

Using SimplePath seems to be the best option for such system as well as fixing the issue, thanks for your reply.

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