Pathfidning AI gets stuck on small objects

Please watch the video

As you can see there is a door, the door is just a model which consists of two parts, collision groups are set up as such that the ai cant even collide with the door but still it sometimes just cannot pathfind over the door when its laying on the ground. Ignore the terrible voice lines I’ll have good ones eventually, anyways the green/red head of the ai represents whether or not they have a line of sight on me, if they do its green otherwise its red.

Here is the code:

task.wait(5)

local shootModule  = require(game.ServerScriptService.NPCShoot)

local PathfindingService = game:GetService("PathfindingService")

local AI = script.Parent
local RP = script.Parent.HumanoidRootPart
local Hum = script.Parent.Humanoid

local Gun = script.Parent:FindFirstChildOfClass("Tool")


local Waypoints = game.Workspace:WaitForChild("Waypoints"):GetChildren()

RP:SetNetworkOwner(nil)

local attackAnim = Hum.Animator:LoadAnimation(script.Attack)
local walkAnim = Hum.Animator:LoadAnimation(script.Walk)
local runAnim = Hum.Animator:LoadAnimation(script.Run)

local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude
rayParams.FilterDescendantsInstances = {Hum}

local Damage = 25
local AttackRange = 15

local shootTrack = AI.Humanoid.Animator:LoadAnimation(game.ReplicatedStorage.Animations.NPCPistolShoot)

walkAnim.Looped = true
runAnim.Looped = true
walkAnim:Play()

local LastSeenPos

local RayParams = RaycastParams.new()
RayParams.FilterType = Enum.RaycastFilterType.Exclude

local pathParams = {
	AgentHeight = 3,
	AgentRadius = 1,
	AgentCanJump = true,
	WaypointSpacing = 2.5,
}

local function getPath(destination)
	
	if typeof(destination) ~= "Vector3" then 
		warn("Destination variable was not a vector3")
	end
	
	
	if destination then
		local path = PathfindingService:CreatePath(pathParams)
		
		path:ComputeAsync(RP.Position , destination)

		return path	
	else
		warn("Pathfinding fail")
		return "Fail"
	end

end

function lineOfSight(target)
	

	
	local rayDirection = target.Position - RP.Position
	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = {RP.Parent}  

	local RayResult = workspace:Raycast(RP.Position, rayDirection, rayParams)

	-- Visualization
	local rayPart = Instance.new("Part")
	rayPart.Size = Vector3.new(0.2, 0.2, rayDirection.Magnitude)
	rayPart.CFrame = CFrame.new(RP.Position, target.Position) * CFrame.new(0, 0, -rayPart.Size.Z / 2)
	rayPart.Anchored = true
	rayPart.CanCollide = false
	rayPart.Color = Color3.new(1, 0, 0) -- Red color for the ray
	rayPart.Transparency = 0.5
	rayPart.Parent = RP.Parent
	rayPart.CanQuery = false
	rayPart.CanTouch = false
	-- Destroy the visualization after a short time
	game:GetService("Debris"):AddItem(rayPart, 0.2)

	if RayResult and RayResult.Instance then
		if RayResult.Instance:IsDescendantOf(target.Parent) then
			--	print("line of sight true")
			script.Parent["Body Colors"].HeadColor3 = Color3.new(0.192157, 1, 0.0666667)
			return true
		else
			--	print("line of sight false")
			script.Parent["Body Colors"].HeadColor3 = Color3.new(1, 0, 0.0156863)
			return false
		end
	end
end


function getTarget()

	local closestTarget = nil
	local distanceFromClosestTarget = 1000000000

	for i, player in pairs(game.Players:GetChildren()) do
		local distance = (player.Character.HumanoidRootPart.Position - RP.Position).Magnitude

		if distance < distanceFromClosestTarget and player.Character.Humanoid.Health > 0 then
			if lineOfSight(player.Character.HumanoidRootPart) then
				if player.Character.Humanoid.Health > 0 then
					distanceFromClosestTarget = distance
					closestTarget = player
				end
			end
		end
	end

	if closestTarget ~= nil then
		return(closestTarget)
	else
		--task.wait(5)
	--	patrol()
	end


end

local db = false

local runAnimPlayingStatus = false
local walkAnimPlayingStatus = false


function chaseTarget(target)
	local path

	path = getPath(target.Character.HumanoidRootPart.Position)
	

	
	if path ~= "Fail" then
	
		local Waypoints = path:GetWaypoints()
		
		if Waypoints[2] then
		
			Hum:MoveTo(Waypoints[2].Position)
			task.spawn(shoot)
			
			if lineOfSight(target.Character.HumanoidRootPart) then
				patrol()
			else
				moveToLastSeen(target.Character.HumanoidRootPart.Position)
			end
			
			
		else
			patrol()
			return
		end

		
	end
	
end

function moveToLastSeen(location)
	local path = getPath(location)

	if path ~= "Fail" then
		for i, waypoint in pairs(path:GetWaypoints()) do

			local humTarget = getTarget()

			if humTarget then
				patrol()
				break
			else
				if walkAnimPlayingStatus == false then
					walkAnim:Play()
					walkAnimPlayingStatus = true
				end
				runAnimPlayingStatus = false
				runAnim:Stop()

				Hum:MoveTo(waypoint.Position)
				Hum.MoveToFinished:Wait()


				if i == #path:GetWaypoints() then
					patrol()
					break
				end
			end
		end
	else
		patrol()
	end

end


function moveTo(target)
	local humTarget = getTarget()
	
	if humTarget then
		chaseTarget(humTarget)
		return
	else
	
		local path = getPath(target)
		
		if path.Status == Enum.PathStatus.Success then
			local Waypoints = path:GetWaypoints()
			for i, waypoint in pairs(Waypoints) do
				
				if i == #path:GetWaypoints() then
					patrol()
					break
				end
				
				 local humTarget = getTarget()

				if humTarget then
					chaseTarget(humTarget)
					break
						
					else
						Hum:MoveTo(waypoint.Position)
						Hum.MoveToFinished:Wait()
				end
			end
		end
	end
end


function patrol()
	local ChosenWapoint = math.random(1, #Waypoints)
	moveTo(game.Workspace.Waypoints:FindFirstChild(ChosenWapoint).Position)
end

script.Parent.Humanoid.Died:Connect(function()
	script:Destroy()
	AI.Head.HeadUI:Destroy()
	Hum.HealthDisplayDistance = 0
end)


local lastPos = RP.Position

function stuckDetect()
	while task.wait(1) do
		if (RP.Position - lastPos).Magnitude < 1 and Gun:GetAttribute("Reloading") == false then
			warn("AI got stuck falling back")
			--RP.Position += RP.CFrame.LookVector * 2
		else
			lastPos = RP.Position
		end
	end
end

function shoot()
	local target = getTarget()
	
	if target and (target.Character.HumanoidRootPart.Position - RP.Position).Magnitude <= 20 and db == false then
		db = true
		shootTrack:Play()
		shootModule.Shoot(target,Gun,AI)
		
		task.wait(Gun:GetAttribute("FireRate"))
		db = false
	end
end

task.spawn(stuckDetect)


patrol()
2 Likes

well first, before we do anything.

make sure to check your navmesh by going into studio settings and enabling the inbuilt navigation mesh and make sure it fully covers objects like that.

let me know what you find if anything.

It was stuck at that moment, It seems like its old patrol path is still there…

alrighty, since roblox’s pathfinding isnt the best, what you can do to avoid this is by using a pathfinding modifier and use the passthrough property to allow the navmesh to completely ignore small objects like that,

let me know if this helped.

1 Like

I’m aware of those modifiers but using them didn’t come to mind, I just want to know was it getting stuck on the door most likely an issue in my code or something wrong with roblox’s pathfinding

edit: it worked and it’s probably the pathfinding, here is the genius ai trying to go over a desk for some unknown reason when it can just walk around it

1 Like

for stuff like that i’d suggest using the costs parameter of agentParameters mixed with a pathfinding modifier, lets say a “desk” cost would be math.huge to prevent the AI from going over it and instead going around it

I have this folder and i made big parts that cover the set of desks how can i make it avoid every part in the folder

image

you have to have an individual pathfinding modifier for each part but you can use a script to pull this off

local folder = script.Parent

for i, part in folder:GetChildren() do
	local pathfindingModifier = Instance.new("PathfindingModifier")
	pathfindingModifier.Parent = part
	pathfindingModifier.Label = "desk"
end

local pathParams = {
	AgentHeight = 1,
	AgentRadius = 1,
	AgentCanJump = false,
	Costs = {}

}

for i, v in pairs(game.Workspace:WaitForChild("AvoidPathing"):GetChildren()) do
	pathParams.Costs[v] = math.huge
end

i did this, it worked, it avoids the desks now, still for some reason walks over them sometimes but it’s good enough for my game

1 Like

Parts had can Queree or however its spelled turned off which caused it to walk over them, i turned that on and it walks over them less frequently, the game will be fast paced so you wont really have AI chases and for that reason I’ll leave it as is. If you see an AI and it shoots you any normal player would kill it and not try to test how good my games code is