Pathfinding being odd

I’m trying to make a pathfinding system, and so far, it seems to work all right when in patrol mode.

The problem is that is very easily breaks if it goes into a hallway that is kinda cramped (but still has enough space to easily go through) or if it has to climb something like a truss or a staircase.

Another problem is that when chasing a player this issue gets even worse, since when so it bumps into walls a lot, gets stuck in walls a lot, and can barely climb a truss or do any jumps without spinning around and breaking

I know roblox’s pathfinding is pretty clunky and hard to code for that reason, but any help is appreciated.

This is my code, it's pretty long and uses functions because I wanted to keep it as clean as possible.
local maxDist = 100
local ischasingplayer = false
local isDebugEnabled = true


local Pathfinding = game:GetService("PathfindingService")
local tw = game:GetService("TweenService")
script.Parent.HumanoidRootPart:SetNetworkOwner(nil)

function findTarget(thing)
	for i,v in pairs(workspace:GetChildren()) do
		if v:IsA"Model" then
			if isDebugEnabled == true then
				print("Model found")
			end
			for i_1,v_1 in pairs(game.Players:GetChildren()) do
				if v.Name == v_1.Name then
					if isDebugEnabled == true then
						print("Turns out, model is player")
					end
					if (v.PrimaryPart.Position - script.Parent.PrimaryPart.Position).Magnitude <= maxDist then
						if isDebugEnabled == true then
							print("Dist was respected")
						end
						ischasingplayer = true
						return v
					else
						ischasingplayer = false
					end
				end
			end
		end
	end
end

function attack(path,wpoints)
	if path then
		if wpoints then
			path.Changed:Connect(function(property: string)
				wait()
				return
			end)
			path.Blocked:Connect(function()
				wait()
				return
			end)
			if path.Status == Enum.PathStatus.Success then
				for i_2, v_2 in pairs(wpoints) do
					--if ischasingplayer == true then
					if i_2 > 1 then
						script.Parent.Humanoid:MoveTo(v_2.Position)
						if isDebugEnabled == true then
							print("Moving To Player")
						end
						if v_2.Action == Enum.PathWaypointAction.Jump then
							script.Parent.Humanoid.Jump = true
						end
						wait(.05)
					else
						if isDebugEnabled == true then
							print("skipped")
						end
					end
					--end
					--script.Parent.Humanoid.MoveToFinished:Wait()
				end
			else
				if isDebugEnabled == true then
					print("path unsuccesful")
				end
				
			end
		end
	end
	return
end
	

function patrol(path,wpoints)
	if path then
		if wpoints then
			path.Changed:Connect(function(property: string)
				return
			end)
			path.Blocked:Connect(function()
				return
			end)
			if path.Status == Enum.PathStatus.Success then
				for i_2, v_2 in pairs(wpoints) do
					if ischasingplayer == false then
						if i_2 > 1 then
							script.Parent.Humanoid:MoveTo(v_2.Position)
							if isDebugEnabled == true then
								print("Moving")
							end
							wait(.3)
						else
							if isDebugEnabled == true then
								print("skipped")
							end
						end
					end
				end
				--script.Parent.Humanoid.MoveToFinished:Wait()
			else
				if isDebugEnabled == true then
					print("path unsuccesful")
				end
			end
		end
	end
end

function chase(v)
	if v then
		local path = Pathfinding:CreatePath(
			{
				AgentRadius = 2;
				AgentHeight = 6;
				AgentCanJump = true;
				AgentCanClimb = true;
				WaypointSpacing = 5;
				Costs = {
					Water = 100;
					BrickWall = math.huge
				}
			}
		)
		
		if isDebugEnabled == true then
			print("Path created")
		end
		if v:IsA("Model") then
			path:ComputeAsync(script.Parent.PrimaryPart.Position, v.PrimaryPart.Position)
		end
		local wpoints = path:GetWaypoints()

		return path,wpoints
	else
		local thing = findWayPoint()
		local path = Pathfinding:CreatePath(
			{
				AgentRadius = 2;
				AgentHeight = 6;
				AgentCanJump = true;
				AgentCanClimb = true;
				WaypointSpacing = 5;
				Costs = {
					Water = 100;
					BrickWall = math.huge
				}
			}
		)

		if isDebugEnabled == true then
			print("Path created towards waypoint")
		end
		
			path:ComputeAsync(script.Parent.PrimaryPart.Position, thing.Position)
		local wpoints = path:GetWaypoints()

		return path,wpoints
		--[[if isDebugEnabled == true then
			print("no player yet")
		end]]
	end
end

function findWayPoint()
	local fold = workspace.Waypoints:GetChildren()
	local waypoint = fold[math.random(1,#fold)]
	return waypoint
end

while wait() do
	if isDebugEnabled == true then
		print("Loop started")
	end
	local player = findTarget()
	local path, wpoints = chase(player)
	if ischasingplayer then
		attack(path,wpoints)
	else
		patrol(path,wpoints)
	end
end

Thank you for your attention.

1 Like

I played around with the script and now it computes the paths almost perfectly when walking, but breaks the moment it has to jump or climb

The code I edited:

function attack(path,wpoints)
	if path then
		if wpoints then
			path.Changed:Connect(function(property: string)
				wait()
				return
			end)
			path.Blocked:Connect(function()
				wait()
				return
			end)
			if path.Status == Enum.PathStatus.Success then
				for i_2, v_2 in pairs(wpoints) do
					--if ischasingplayer == true then
					if i_2 > 1 then
						if isDebugEnabled == true then
							print("Moving To Player")
						end
						if v_2.Action == Enum.PathWaypointAction.Jump then
							script.Parent.Humanoid.Jump = true
						elseif v_2.Action == Enum.PathWaypointAction.Walk then
							script.Parent.Humanoid:MoveTo(v_2.Position)
							wait(.1)
							return
						end
						wait(.75)
					else
						if isDebugEnabled == true then
							print("skipped")
						end
					end
					--end
					--script.Parent.Humanoid.MoveToFinished:Wait()
				end
			else
				if isDebugEnabled == true then
					print("path unsuccesful")
				end
				
			end
		end
	end
	return
end
1 Like

Breaks as in fails to perform the jump or climbing motion, or does it not calculate the path correctly. A video would be appreciated!

Breaks as in fails to perform the jump or climbing motion.

Here’s a video:

(Sorry for late response, I was in school. Also sorry for the video quality, the file was too large and I compressed it a bit too much.)

If you’re serverside, instead of setting the Humanoid.Jump property, use Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)

It didn’t change anything, but I think it has to do with the “return” I placed, since it resets the function

Another thing I should note is that the patroling state and the chasing state are 2 different functions, and that for some reason it can’t jump no matter what, but when patrolling it can climb, unlike when chasing a player

Does it have a jumpPower greater than zero? Additionally, try removing the return.

The JumpPower has remained unchanged (50) and it can jump if I go to server side and change the jump value

And removing the return will prevent the rig from computing the pathfinding correctly, since it’ll go to the player’s position when it was detected and then it would only compute the player’s position after it reached the previous one

I just found something interessting…
The reason why it doesn’t jump or climb is (probably) because it gets stuck at waypoint 2!

image
(the “2” is the waypoint number)

I tweaked a little bit the code and it litteraly started floating in place lol

Have you gotten it to jump yet?

Yeah, it is a little less accruate than before, but still works about fine. Thank you very much for your help! (Sorry for late response)

What was the solution and problem? It helps if it is clearly defined so future developers with these issues can use this as a learning experience

It was actually the return that was breaking, since it was resetting the function each time, so I made it check what instance of waypoint it was and return only if it exceeded a certain value

Final code:

local maxDist = 255 -- How close does the player have to be in order for the bot to detect
local ischasingplayer = false -- DO NOT CHANGE
local isDebugEnabled = true -- If true, the script will log every action in the Output tab (or dev console if not in studio)
local requiresLineOfSight = true -- UNUSED
local impVal = 4 -- Amount of waypoints before reset
local CanJump = true -- Should entity be able to jump (setting this to false and expecting your entity to jump will completly break the pathfinding)
local CanClimb = true -- Same as last one but applies with climbing instead (If set to true it will, unless with a pathfinding link, only go up trusses!)
local Width = 2 -- How wide should the entity be considered according to the pathfinding, since it isn't automaticly set
local Height = 6 -- Same as last one but applies with height
local WaypointSpacingValue = 5 -- How spaced out are the pathfinding's waypoints (Without these it can't compute a correct path)
local WaypointsFold = workspace.Waypoints -- Waypoints for the entity to go to when there isn't a player

local movecooldown = 1.6/script.Parent.Humanoid.WalkSpeed -- DO NOT CHANGE

local Pathfinding = game:GetService("PathfindingService")
script.Parent.HumanoidRootPart:SetNetworkOwner(nil)

function findTarget(thing)
	for i,v in pairs(workspace:GetChildren()) do
		if v:IsA"Model" then
			if isDebugEnabled == true then
				print("Model found")
			end
			for i_1,v_1 in pairs(game.Players:GetChildren()) do
				if v.Name == v_1.Name then
					if isDebugEnabled == true then
						print("Turns out, model is player")
					end
					if (v.PrimaryPart.Position - script.Parent.PrimaryPart.Position).Magnitude <= maxDist then
						if isDebugEnabled == true then
							print("Dist was respected")
						end
						ischasingplayer = true
						return v
					else
						ischasingplayer = false
					end
				end
			end
		end
	end
end

function attack(path,wpoints)
	if path then
		if wpoints then
			path.Changed:Connect(function()
				wait(movecooldown)
				return
			end)
			path.Blocked:Connect(function()
				wait(movecooldown)
				return
			end)
			if path.Status == Enum.PathStatus.Success then
				for i_2, v_2 in pairs(wpoints) do
					--if ischasingplayer == true then
					if i_2 > 1 and i_2 <= impVal then
						if isDebugEnabled == true then
							print("Moving To Player")
						end
						--script.Parent.Humanoid:MoveTo(v_2.Position)
						if v_2.Action == Enum.PathWaypointAction.Jump then
							script.Parent.Humanoid.Jump = true
							if isDebugEnabled == true then
								warn("Succesfully jumped")
							end
							wait(movecooldown*(1/(WaypointSpacingValue/5)))
						elseif v_2.Action == Enum.PathWaypointAction.Walk then
							script.Parent.Humanoid:MoveTo(v_2.Position)
							if isDebugEnabled == true then
								warn("FOLLOWS INTENDED PATH "..i_2)
							end
							wait(movecooldown*(1/(WaypointSpacingValue/5)))
							continue
						else
							script.Parent.Humanoid:MoveTo(v_2.Position)
							if isDebugEnabled == true then
								warn("ok what")
							end
							wait(movecooldown*(1/(WaypointSpacingValue/5)))
						end
						--wait(.75)
						--wait(.1)
						--return
					elseif i_2 > impVal then
						if isDebugEnabled == true then
							print("Moving To Player")
						end
						--script.Parent.Humanoid:MoveTo(v_2.Position)
						if v_2.Action == Enum.PathWaypointAction.Jump then
							script.Parent.Humanoid.Jump = true
							if isDebugEnabled == true then
								warn("Succesfully jumped")
							end
							wait(movecooldown*(1/(WaypointSpacingValue/5)))
						elseif v_2.Action == Enum.PathWaypointAction.Walk then
							script.Parent.Humanoid:MoveTo(v_2.Position)
							if isDebugEnabled == true then
								warn("FOLLOWS INTENDED PATH "..i_2)
							end
							wait(movecooldown*(1/(WaypointSpacingValue/5)))
							break
						else
							script.Parent.Humanoid:MoveTo(v_2.Position)
							if isDebugEnabled == true then
								warn("ok what")
							end
							wait(movecooldown*(1/(WaypointSpacingValue/5)))
						end
						--wait(.75)
						--wait(.1)
						--return
					
					else
						if isDebugEnabled == true then
							print("skipped")
						end
					end
					--end
					--script.Parent.Humanoid.MoveToFinished:Wait()
				end
			else
				if isDebugEnabled == true then
					print("path unsuccesful")
				end
				
			end
		end
	end
end
	

function patrol(path,wpoints)
	if path then
		if wpoints then
			path.Changed:Connect(function(property: string)
				return
			end)
			path.Blocked:Connect(function()
				return
			end)
			if path.Status == Enum.PathStatus.Success then
				for i_2, v_2 in pairs(wpoints) do
					if ischasingplayer == false then
						if i_2 > 1 then
							script.Parent.Humanoid:MoveTo(v_2.Position)
							if isDebugEnabled == true then
								print("Moving")
							end
							if v_2.Action == Enum.PathWaypointAction.Jump then
								--script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
								script.Parent.Humanoid.Jump = true
							end
							
							--[[if ischasingplayer == true then
								return
							end]]
							wait(movecooldown*(3/(WaypointSpacingValue/5)))
						else
							if isDebugEnabled == true then
								print("skipped")
							end
						end
					end
				end
				--script.Parent.Humanoid.MoveToFinished:Wait()
			else
				if isDebugEnabled == true then
					print("path unsuccesful")
				end
			end
		end
	end
	return
end

function chase(v)
	if v then
		local path = Pathfinding:CreatePath(
			{
				AgentRadius = Width;
				AgentHeight = Height;
				AgentCanJump = CanJump;
				AgentCanClimb = CanClimb;
				WaypointSpacing = WaypointSpacingValue;
				Costs = {
					Water = 100;
					BrickWall = math.huge
				}
			}
		)
		
		if isDebugEnabled == true then
			print("Path created")
		end
		if v:IsA("Model") then
			path:ComputeAsync(script.Parent.PrimaryPart.Position, v.PrimaryPart.Position)
		end
		local wpoints = path:GetWaypoints()

		return path,wpoints
	else
		local thing = findWayPoint()
		local path = Pathfinding:CreatePath(
			{
				AgentRadius = Width;
				AgentHeight = Height;
				AgentCanJump = CanJump;
				AgentCanClimb = CanClimb;
				WaypointSpacing = WaypointSpacingValue;
				Costs = {
					Water = 100;
					BrickWall = math.huge
				}
			}
		)

		if isDebugEnabled == true then
			print("Path created towards waypoint")
		end
		
			path:ComputeAsync(script.Parent.PrimaryPart.Position, thing.Position)
		local wpoints = path:GetWaypoints()

		return path,wpoints
		--[[if isDebugEnabled == true then
			print("no player yet")
		end]]
	end
end

function findWayPoint()
	local fold = WaypointsFold:GetChildren()
	local waypoint = fold[math.random(1,#fold)]
	return waypoint
end

while wait() do
	movecooldown = 0.00625*script.Parent.Humanoid.WalkSpeed
	if isDebugEnabled == true then
		print("Loop started")
	end
	local player = findTarget()
	local path, wpoints = chase(player)
	if ischasingplayer then
		attack(path,wpoints)
	else
		patrol(path,wpoints)
	end
end
1 Like

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