Pathfinding AI unable to catch up to player

Basically, my AI walks around perfectly, no stuttering or anything. When it senses a player nearby, it pathfinds to the player, it all looks smooth and clean, but as soon as the AI gets within like 6 studs of the player, it starts to stutter. I’ve set the NetworkOwnership of ALL parts within the monster to server, and I just can’t find anything about fixing this. It’s ONLY when he gets near the player he does it.

I’m working on a big game and I really need help with this problem. I know this script is kind of big, but I just can’t figure out why he is stuttering.

local char = script.Parent
local hum = char.Humanoid
local rep = game:GetService("ReplicatedStorage")
local changeMusic = rep:FindFirstChild("chaseMusic")
local waypoints = workspace.Waypoints:GetChildren()

local PathfindingService = game:GetService("PathfindingService")

local runSpeed = 35
local walkSpeed = 20

local pastTarget
local lostTarget = true


local function canSeeTarget(target)
	local origin = char.Torso.Position
	local direction = (target.HumanoidRootPart.Position - char.Torso.Position).unit * 120
	local ray = Ray.new(origin, direction)
	
	local hit, pos = workspace:FindPartOnRay(ray, char)
	
	if hit then
		if hit:IsDescendantOf(target) then
			return true
		end
	else
		return false
	end
end

local function findTarget()
	local players = game.Players:GetPlayers()
	local maxDistance = 60
	local scareDist = 15
	local nearestTarget
	
	for index, player in pairs(players) do
		if player.Character then
			local target = player.Character
			local dist = (char.Torso.Position - target.HumanoidRootPart.Position).Magnitude
			
			if dist < maxDistance and canSeeTarget(target) then
				nearestTarget = target
				maxDistance = dist
			elseif dist < scareDist then
				scareDist = dist
				nearestTarget = target
			end
		end
	end
	
	return nearestTarget
end


local function getPath(destination)
	local pathParams = {
		AgentHeight = 3,
		AgentRadius = 2,
		AgentCanJump = false,
		AgentCanClimb = false,
		Costs = {
			Walls = math.huge
		}
	}
	local path = PathfindingService:CreatePath(pathParams)
	
	path:ComputeAsync(char.Torso.Position, destination.Position)
	
	return path	
end

--THIS IS WHERE HE WALKS TO THE PLAYER
local function attack(target) 
	local dist = (char.Torso.Position - target.HumanoidRootPart.Position).Magnitude
	print("attac")
	if dist > 7 then
		local path = getPath(target.HumanoidRootPart)

		if path.Status == Enum.PathStatus.Success then
			for index, waypoint in pairs(path:GetWaypoints()) do
					local target = findTarget()
				
					print("FINDING PLAYER")
					hum.WalkSpeed = runSpeed
					
					hum:MoveTo(waypoint.Position)
					hum.MoveToFinished:Wait()
				
			end
		else
			hum:MoveTo(target.HumanoidRootPart.Position - char.Torso.CFrame.LookVector * 10)
		end
		
		
	else
		target.Humanoid.Health = 0
		if target.Humanoid.Health <= 0 then
			rep.killPlayer:FireClient(game.Players:GetPlayerFromCharacter(target))
			char.Head.Kill:Play()
			char.Head.Kill2:Play()
		end
		print("Killed ", target.Name)
	end
end

local function walkTo(destination)
	local path = getPath(destination)
	
	if path.Status == Enum.PathStatus.Success then
		for index, waypoint in pairs(path:GetWaypoints()) do
			local target = findTarget()
			if target then
				print("TARGET FOUND - ", target.Name)
				
				if target and target.Humanoid.Health > 0 then
					hum.WalkSpeed = runSpeed
					attack(target)
					--walkTo(attack())
					pastTarget = game.Players:GetPlayerFromCharacter(target)
					lostTarget = false
					changeMusic:FireClient(game.Players:GetPlayerFromCharacter(target), true)
				end
				break
			else
				print("Patrolling")
				hum.WalkSpeed = walkSpeed
				if pastTarget and lostTarget == false then
					print(pastTarget.Name)
					lostTarget = true
					changeMusic:FireClient(pastTarget, false)
					print("Fired Stop")
					pastTarget = nil
				end
				hum:MoveTo(waypoint.Position)
				hum.MoveToFinished:Wait()
			end
		end
	else
		hum:MoveTo(destination.Position - char.Torso.CFrame.LookVector * 10)
	end
end

local function patrol()
	local waypoints = workspace.Waypoints:GetChildren()
	walkTo(waypoints[math.random(1, #waypoints)])
end

while true do
	task.wait(0.1)
	patrol()
end

This one guy had another post on it, and had a really good video of what is also happening to me, but I can’t figure out how he fixed it. Here is the video of what is happening for me and him.

Post: Pathfinding AI stutters when it nearly approaches player
Video:


See how the AI is smooth until it gets close to the player, most of the time not even REACHING the player at all!

2 Likes

What i think its doing there is that is creating more paths while the player is close to it, so it looks that it sttuters. In that case you could just make it so it follows the player at that point, like:

-- this is just an example

if distance > 7 then
    -- code for pathfinding
else
    aiChar:MoveTo(playerCharacter)
end
1 Like

The whole point of distance < 7 is that the player should be killed because he’s so close

3 Likes

My terrible explanation:
Basically your NPC is stuttering because it’s updating to the old position of the player, and I’d assume it does a distance check once when the pathfinding interval updates. As the position of the player is presumed everchanging, a semi-decent workaround would be to either determine where the Player is going with rootPart.AssemblyLinearVelocity or humanoid.MoveDirection, OR when the NPC is within raycast distance (Meaning there’s no objects from and to the NPC and player) you can use the humanoid:MoveTo(position, playerRootPart) making the NPC walk to that position and follow the root part if it moves.

So yeah, every interval of check the player has already walked out of that radius, and the NPC gets to that position before the next interval starts causing it to stop then start running really fast, as you’ve called it stuttering.


Now that I’ve read your script, you’re not actually using the canSeeTarget() function from what I can see and your :MoveTo() functions are missing the second parameter, maybe it’ll be a more shrimple solve than you think.

2 Likes

That shouldn’t work though. Since the pathfinding script is on the server, it also checks the players position on the server. This means that since it’s checking on the server, it only kills the player on the player server location, so that shouldn’t work.

And I would have no idea how to fix that, if you really think it will work, then if you can, send me the updated code. This is my first ever pathfinding script.

1 Like

Well, the server is constantly being updated with the player position, so when the NPC is getting to the position (on the server) it’s already been updated with new positional coords.

I can’t be going through your full script and repost it, for the most part it looks fine, you’ll just need to do some messing around. See if adding that second parameter to the :MoveTo() method does anything first, no need to overcomplicate it just yet. And if that doesn’t work I could probably guide or give some extra ideas on how you can accomplish guessing a player’s location (This is so you’re learning instead of someone script bombing you and not learning anything).

Thanks, yeah I would like to learn this.

1 Like

So, the monster tries to pathfind to the player. Since I’m using pathfinding the monster uses char:MoveTo(Waypoint), now the problem is that each Waypoint isnt a instance. so i cant add 2nd parameter

Make the second parameter the player/target’s root part, since that’s the primary target of the NPC.

nevermind i should have read the other posts

Well simply put: the player’s position on the client is “ahead” of of the player’s position on the server. On the server the AI is touching the player, which is why it stutters.

I suggest you take a look at this post here: AI unable to catch up to player - #60 by Superdestructo09

Yeah, so if the AI is touching the player on the server, why doesn’t the AI kill the player then? The AI should kill him if he is touching like you say he is, and he doesn’t run the line of code.

1 Like

Well, also inside your script you only try attacking the player when you initiate the attack function. You’re only checking one time for the distance check for if the NPC is in range, so it waits till it gets all the way through the waypoints before seeing if the target is within range. So you might want to check at every waypoint being finished whether the target is within range.

local function canAttack(target)
    if not (target and target.Parent) then return end
    local dist = (char.Torso.Position - target.HumanoidRootPart.Position).Magnitude

    if (dist <= 7) then
        -- initiate your attack
    end
end

-------------------
--- This is within your attack(target) function
for index, waypoint in pairs(path:GetWaypoints()) do
    -- you might want to use this to check if your target is still valid (imo)
	local target = findTarget() 
				
	print("FINDING PLAYER")
	hum.WalkSpeed = runSpeed
					
	hum:MoveTo(waypoint.Position)
	hum.MoveToFinished:Wait()
    -- // Right here, we're now always checking if the target is within range
	canAttack(target) 
end

I got this for my box but all I did was remove the wait and make it super small. It’s stuttering because the distance it’s moving is so small then you have to compute the path and wait a bit which gives of an obvious pause. Then this cycle repeats.

1 Like