How to make pathfinding npc more efficient?

I am trying to make an enemy npc that roams my map and can open doors, and chase the player when they get close enough, it sort of works but its very inconsistent. Sometimes he just sits and looks at the player before eventually deciding to chase them, and a lot of times he wont roam to a navigation point (parts around the map that he randomly picks and moves to when he cant find a target so he looks like he is patrolling the map) because the door is 1 stud too low, and I made him unable to jump so he just doesn’t go to the path, leading to a long chain of failed paths meaning he will just sit still for a while. When the player sees the enemy most times he wont even react and when he finally does I run into the previously mentioned issue (it might be the map itself, or the agentparams). I am just wondering how I could make this code run more consistently and make it less messy?

local hum = script.Parent:WaitForChild("Humanoid")
local root = script.Parent:WaitForChild("HumanoidRootPart")
local head = script.Parent:WaitForChild("Head")

root:SetNetworkOwner(nil)

local chase = script.Parent:WaitForChild("Chase")
local chaseAnim = hum:LoadAnimation(chase)
chaseAnim.Priority = Enum.AnimationPriority.Action

local notice1 = script.Parent.UpperTorso.Notice1
local notice2 = script.Parent.UpperTorso.Notice2
local notice3 = script.Parent.UpperTorso.Notice3
local notice4 = script.Parent.UpperTorso.Notice4
local notice5 = script.Parent.UpperTorso.Notice5
local notice6 = script.Parent.UpperTorso.Notice6
local notice8 = script.Parent.UpperTorso.Taunt2
local notice9 = script.Parent.UpperTorso.Taunt3
local notice10 = script.Parent.UpperTorso.Taunt4
local notice11 = script.Parent.UpperTorso.Taunt5
local notice12 = script.Parent.UpperTorso.Taunt6
local notice13 = script.Parent.UpperTorso.Taunt7
local notice14 = script.Parent.UpperTorso.Taunt8
local notice15 = script.Parent.UpperTorso.Taunt9

local sounddb = false

local noticesounds = {notice1, notice2, notice3, notice4, notice5, notice6, notice8, notice9, notice10, notice11, notice12, notice13, notice14, notice15}


local visualizepoints = true

for _, door_part in pairs(workspace.Doors:GetDescendants()) do
	if door_part:IsA("BasePart") or door_part:IsA("MeshPart") then
		local modifier = Instance.new("PathfindingModifier")
		modifier.ModifierId = "Door"
		modifier.PassThrough = true
		modifier.Parent = door_part
	end
end

local agentparams = {
	AgentRadius = 1.8,
	AgentHeight = 4,
	AgentCanJump = false,
	Costs = {
		Door = 0
	}
}

function idle()
	local navParts = workspace.NavParts:GetChildren()
	local goal = navParts[math.random(1, #navParts)].Position
	
	wait(math.random(1, 5))
	
	local path = game:GetService("PathfindingService"):CreatePath(agentparams)
	path:ComputeAsync(root.Position, goal)
	local waypoints = path:GetWaypoints()
	
	if visualizepoints == true then
		for i, waypoint in pairs(waypoints) do
			local part = Instance.new("Part")
			part.Shape = "Ball"
			part.Material = "Neon"
			part.Size = Vector3.new(0.6, 0.6, 0.6)
			part.Position = waypoint.Position + Vector3.new(0, 2, 0)
			part.Anchored = true
			part.CanCollide = false
			part.Parent = workspace
			game:GetService("Debris"):AddItem(part, 5)
		end
	end
	
	if path.Status == Enum.PathStatus.Success then
		for _, waypoint in ipairs(waypoints) do
			hum:MoveTo(waypoint.Position)
			local timeOut = hum.MoveToFinished:Wait()
			if not timeOut then
			print("I'm stuck :(")
			idle()
			end
		end
	else
		print("Path failed :(")
		wait(1)
		locateTarget()
		idle()
	end
end

function locatePath(target)
	local path = game:GetService("PathfindingService"):CreatePath(agentparams)
	path:ComputeAsync(root.Position, target.Position)
	local waypoints = path:GetWaypoints()
	
	if visualizepoints == true then
		for i, waypoint in pairs(waypoints) do
			local part = Instance.new("Part")
			part.Shape = "Ball"
			part.Material = "Neon"
			part.Size = Vector3.new(0.6, 0.6, 0.6)
			part.Position = waypoint.Position + Vector3.new(0, 2, 0)
			part.Anchored = true
			part.CanCollide = false
			part.Parent = workspace
			game:GetService("Debris"):AddItem(part, 5)
		end
	end
	
	if path.Status == Enum.PathStatus.Success then
		for _, waypoint in ipairs(waypoints) do
			hum:MoveTo(waypoint.Position)
			local timeOut = hum.MoveToFinished:Wait(1)
			if not timeOut then
				print("Path took too long to compute :(")
				locatePath(target)
				break
			end
			if checksight(target) then
				repeat
					print("Moving directly to target!")
					hum:MoveTo(target.Position)
					attack(target)
					wait(0.1)
					if target == nil then
						break
					elseif target.Parent == nil then
						break
					end
				until checksight(target) == false or hum.Health < 1 or target.Parent.Humanoid.Health < 1
				break
			end
			if (root.Position - waypoints[1].Position).magnitude > 20 then
				print("Target has moved! Time to generate a new path >:)")
				locatePath(target)
				break
			end 
		end
	end
end

function checksight(target)
	local ray = Ray.new(root.Position, (target.Position - root.Position).Unit * 40)
	local hit, position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent})
	if hit then
		if hit:IsDescendantOf(target.Parent) and math.abs(hit.Position.Y - root.Position.Y) < 3 then
			print("Target located!")
			return true
		end
	end
	return false
end

function locateTarget()
	local dist = 40
	local target = nil
	local potentialTargets = {}
	local seeTargets = {}
	for i, v in ipairs(workspace:GetChildren()) do
		local human = v:FindFirstChild("Humanoid")
		local torso = v:FindFirstChild("Torso") or v:FindFirstChild("HumanoidRootPart")
		if human and torso and v.Name ~= script.Parent.Name then
			if (root.Position - torso.Position).magnitude < dist and human.Health > 0 then
				table.insert(potentialTargets, torso)
			end
		end
	end
	if #potentialTargets > 0 then
		for i, v in ipairs(potentialTargets) do
			if checksight(v) then
				table.insert(seeTargets, v)
			elseif #seeTargets == 0 and (root.Position - v.Position).magnitude < dist then
				target = v
				dist = (root.Position - v.Position).magnitude
			end
		end
	end
	if #seeTargets > 0 then
		dist = 40
		for i, v in ipairs(seeTargets) do
			if (root.Position - v.Position).magnitude < dist then
				target = v
				dist = (root.Position - v.Position).magnitude
			end
		end
	end
	if target then
		noticesounds[math.random(1, #noticesounds)]:Play()
	end
	return target
end

function attack(target)
	if (root.Position - target.Position).magnitude < 2 then
		script.Parent.HumanoidRootPart.Kill:Play()
		if target.Parent ~= nil then
			target.Parent.Humanoid:TakeDamage(9999)
			if chaseAnim.IsPlaying == true then
				chaseAnim:Stop()
			end
		end 
		wait(0.4)
	end
end


function main()
	local target = locateTarget()
	if target then
		hum.WalkSpeed = 13
		if chaseAnim.IsPlaying == false then
			chaseAnim:Play()
		end
		locatePath(target)
	else
		hum.WalkSpeed = 8
		if chaseAnim.IsPlaying == true then
			chaseAnim:Stop()
		end
		idle()
	end
end

while wait(0.1) do
	if hum.Health < 1 then
		break
	end
	main()
end
2 Likes