Pathfinding service gives completely different waypoints

So I’m working on a pathfinding AI that has 3 simple stages.

  1. It attempts to Raycast to every player in order from closest to furthest. If it doesn’t find a player, it will choose a random waypoint (Part in a folder in the workspace) to pathfind to. It will loop this indefinitely until it finds a player. [Roaming]

  2. If a player is found, it will immediately stop the Roaming and will move in the player’s direction. [Chasing]

  3. If a player has just been chased, but the AI loses sight of them (failed Raycast), it will pathfind to the last location of the player. [Following]

The issue I’m having is when the AI enters the ‘Following’ stage, a path is made (indicated by the drawn out parts in the video) but the AI doesn’t seem to be following the waypoints. I am using coroutines to close the old Move() function that was active during ‘Roaming’ (Shown in the code) so maybe there’s an issue with that? I’ve been working on this for 2 days now and can’t seem to wrap my head around it. Any help would be appreciated.

Here is a screenshot of the Output. It shows the waypoint positions from the pathfinding printed out right after the AI enters the ‘Following’ mode. As you can see, the vectors are drastically different, and not consistent with the distance you’d get between 2 Waypoints.

image

Here is my code. It’s quite long, but the coroutines that call the Pathfind() function are in the Roam() function, and the while loop at the bottom.

-- {{Services}} --
local PlayerService = game:GetService("Players")
local PathfindingService = game:GetService("PathfindingService")


-- {{Settings}} --
local walkSpeed = 12
local runSpeed = 14
local rangeShort = 40
local rangeFar = 70
local pathfindParams = {
	["AgentHeight"] = 7,
	["AgentRadius"] = 6,
	["AgentCanJump"] = false,
}


-- {{Variables}} --
local AI = "Idle"
local Cooldown = false
local CanSeePlayer = false
local Speed = walkSpeed
local Range = rangeShort
local PlayerBuffer


-- {{Objects}} --
local model = script.Parent.Parent
local humanoid = model:WaitForChild("Humanoid")
local humanoidRootPart = model:WaitForChild("HumanoidRootPart")
humanoidRootPart:SetNetworkOwner(nil)
local coroF
local coroR





-----------------------------------------------------------------------------------------------


function Delete()
	for i, part in pairs(workspace.temp:GetChildren()) do
		part:Destroy()		
	end	
end


function Move(path)
	if path.Status ~= Enum.PathStatus.Success then return end
	local waypoints = path:GetWaypoints()
	
	for i, waypoint in pairs(waypoints) do
		local part = Instance.new("Part")
		part.Name = i
		part.Shape = Enum.PartType.Ball
		part.Position = waypoint.Position
		part.Material = Enum.Material.Neon
		part.Size = Vector3.new(0.3,0.3,0.3)	
		part.Anchored = true
		part.CanCollide = false
		part.BrickColor = BrickColor.new("Neon green")
		part.Parent	= workspace.temp
	end
	
	for i, waypoint in pairs(waypoints) do
		if i > 1 then -- Start on second waypoint
			
			print(waypoint.Position)
			humanoid:MoveTo(waypoint.Position)
			humanoid.MoveToFinished:Wait()
			
		end
	end
	
	Delete()
	AI = "Idle"
	Cooldown = false
end





-----------------------------------------------------------------------------------------------





function Pathfind(target)
	humanoid:Move(Vector3.new())
	
	if Cooldown == false then
		Cooldown = true

		local origin = humanoidRootPart.Position
		
		local path = PathfindingService:CreatePath(pathfindParams)
		
		path:ComputeAsync(origin, target)
		Move(path)
	end
end





-----------------------------------------------------------------------------------------------





function GetPlayersInOrder()
	local players = PlayerService:GetPlayers()
	
	local dT = {}
	local pT = {}
	
	for _, player in pairs(players) do
		local charcter = player.Character or player.CharacterAdded:Wait()
		if charcter then
			local hrp = charcter:WaitForChild("HumanoidRootPart")
			
			local origin = humanoidRootPart.Position
			local target = hrp.Position
			local distance = (target - origin).Magnitude
			
			pT[distance] = player
			table.insert(dT, distance)
		end
	end
	
	table.sort(dT, function(a,b)
		return a < b
	end)
	
	return dT, pT
end





-----------------------------------------------------------------------------------------------





function FindPlayer()
	local dT, pT = GetPlayersInOrder()
	
	for _, distance in pairs(dT) do
		local player = pT[distance]
		local character = player.Character or player.CharacterAdded:Wait()
		if character then
			if distance <= Range then
				local RayParams = RaycastParams.new()
				RayParams.FilterDescendantsInstances = {model}
				RayParams.FilterType = Enum.RaycastFilterType.Exclude
				local hrp = character:WaitForChild("HumanoidRootPart")
				local origin = humanoidRootPart.Position
				local target = hrp.Position
				local direction = (target - origin).Unit

				local RaycastResult = workspace:Raycast(origin, direction * Range, RayParams)
				
				if RaycastResult then
					if RaycastResult.Instance:IsDescendantOf(character) then
						CanSeePlayer = true
						return player
					end
				end
			end
		end
	end
	CanSeePlayer = false
end





-----------------------------------------------------------------------------------------------





function MoveToPlayer(player)
	local character = player.Character or player.CharacterAdded:Wait()
	if character then
		local hrp = character:WaitForChild("HumanoidRootPart")
		local origin = humanoidRootPart.Position
		local target = hrp.Position
		local direction = (target - origin).Unit
		
		humanoid:Move(direction)
		AI = "Chase"
		
		if coroR then
			coroutine.close(coroR)
			Delete()
		end
	end
end





-----------------------------------------------------------------------------------------------





function Roam()
	local waypoints = game.Workspace:WaitForChild("LoopPoints"):GetChildren()
	local randomPoint = waypoints[math.random(1, #waypoints)]
	coroR = coroutine.create(Pathfind)
	
	coroutine.resume(coroR, randomPoint.Position)
end





-----------------------------------------------------------------------------------------------




while true do
	local player = FindPlayer()
	
	
	if player then
		
		if PlayerBuffer ~= player and coroF then -- If the player has changed
			coroutine.close(coroF)
			Delete()
		end
		
		PlayerBuffer = player
		
		MoveToPlayer(player)
		
	else
		
		if AI == "Idle" then
			Roam()
		end
		
	end
	
	
	--------------------------------------------------------------------------
	
	
	if PlayerBuffer and player == nil then -- Player has hid and no new player
		local character = PlayerBuffer.Character or PlayerBuffer.CharacterAdded:Wait()
		if character then
			Cooldown = false
			AI = "Following"
			local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
			local target = humanoidRootPart.Position

			PlayerBuffer = nil
			
			if coroR then
				coroutine.close(coroR)
				Delete()
			end
			
			coroF = coroutine.create(Pathfind)
			
			coroutine.resume(coroF, target)
		end
	end
	
	print(AI)
	
	wait()
end

Ah, I’ve solved this one. Turns out there is a new coroutine being made every time the Roam() function is called. To fix it, I just needed to set coroR to nil everytime the coroutine was closed and create the coroutine only if coroR == nil.

Basically, if you ever need to use variable coroutines in conditional loops, create a cooldown so that the coroutine.create() line is only run once.

local coro

function Test(text)
	while true do
		print(text)
		task.wait()
	end
end


local i = 0
local toggle = false

while true do
	i = i + 1
	
	if i == 50 then
		i = 0
		
		if toggle == false then
			toggle = true
			
			if coro == nil then
				coro = coroutine.create(Test)
				coroutine.resume(coro, "Hi")
			end
		else
			toggle = false
			
			if coro then
				coroutine.close(coro)
				coro = nil
			end
		end
	end
	
	task.wait()
end

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