Help with pathfinding

Alright, so I am trying to make a pathfinding script where the enemy chases the player when in a certain radius. However, the enemy will get stuck on walls when chasing the player. The enemy (when not chasing the player) goes to a random part in a folder called waypoints. when it is going to a random part, it works perfectly fine, however, when I tried to copy that bit over into the chase part, it just broke the script.

local enemy2 = script.Parent
local humanoid = enemy2.Humanoid
local PathFindingService = game:GetService(“PathfindingService”)
enemy2.PrimaryPart:SetNetworkOwner(nil)

local function findTarget()
local players = game.Players:GetPlayers()
local maxDistance = 30
local nearestTarget

for index, player in pairs(players) do
	if player.Character then
		local target = player.Character
		local distance = (enemy2.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
		
		if distance < maxDistance then
			nearestTarget = target
			maxDistance = distance
		end
	end
end

return nearestTarget	

end

local function getPath(destination)

local pathParams = {
	["AgentHeight"] = 6,
	["AgentRadious"] = 9,
	["AgentCamJump"] = false
}
local path = PathFindingService:CreatePath(pathParams)

path:ComputeAsync(enemy2.HumanoidRootPart.Position, destination.Position)

return path

end

local function attack(target)
local distance = (enemy2.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
if distance > 6 then
humanoid:MoveTo(target.HumanoidRootPart.Position)
else
local attackAnim = humanoid:loadAnimation(script.Attack)
attackAnim:Play()
target.Humanoid.Health = (target.Humanoid.Health - 10)
wait(1)
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 and target.Humanoid.Health > 0 then
		print("TARGET FOUND!", target.Name)
			attack(target)
			enemy2.Humanoid.WalkSpeed = 17
			break
		else
		print("Moving to ", waypoint.Position)
	humanoid:MoveTo(waypoint.Position)	
			humanoid.MoveToFinished:Wait(0.1)	
			enemy2.Humanoid.WalkSpeed = 8
		end
		end
else
	humanoid:MoveTo(destination.Position - (enemy2.HumanoidRootPart.CFrame.LookVector * 10))
	end
end

local function wander()
local waypoints = workspace.waypoints:GetChildren()
local randomNum = math.random(1, #waypoints)
walkTo(waypoints[randomNum])

end
while wait(0) do

wander()

end

Anyone able to help me fix this?

1 Like

What does this mean? I’m assuming your original problem is the NPC gets stuck on obstacles, is there another problem youre facing?

To address the NPC getting stuck on obstacles problem, you can call a coroutine function whenever the NPC is moving, this function runs independent of the other functions and is used to check if the NPC is stuck.

You can do this with a while loop that compares the NPC’s previous position with its new position, if the difference between the two positions is less than (let’s say) 1, we know that the NPC must be stuck on an obstacle then.

Here’s a piece of code I had written previously dealing with the matter, I’m sure it could be better improved but you get a general idea.

	local CheckIfNPCStuck = coroutine.wrap(function()	-- Check if NPC is stuck on something
		while true do
			if Guard.Humanoid.Chasing.Value == false then	-- This is a bool which is false when the guard is not chasing the player
				break
			end
			local PreviousNPC_Position = Guard.HumanoidRootPart.Position	-- NPC old position
			wait(0.5)	-- Check interval
			local CurrentNPC_Position = Guard.HumanoidRootPart.Position	-- NPC position after 0.5 seconds
			if (PreviousNPC_Position - CurrentNPC_Position).magnitude<1 and not Stuck then	-- NPC must be stuck, as it has moved less than 1 in 0.5 seconds
				--print(Guard.Name .. " Stuck")
				Stuck = true
				Path:ComputeAsync(CurrentNPC_Position, player.Character.HumanoidRootPart.Position)	-- Create a new path towards player with the obstacle in mind
				
				local waypoints = Path:GetWaypoints()
				local Count = 0
				for _, waypoint in pairs(waypoints) do	-- Loop through waypoints
					if Count  == (#waypoints/2) then	-- only travel through half the waypoints to unstick NPC
						break
					end
					Count += 1
					Guard.Humanoid:MoveTo(waypoint.Position)
					Guard.Humanoid.MoveToFinished:Wait()
				end
				Stuck = false	-- Set stuck to false
			end
		end
	end)
1 Like

Here, you can use my script that I made some while ago for my enemies.
It uses a heavily modified version of pathfinding that Roblox provides in their tutorial.
There may be some bugs but overall it actually works perfectly and the enemy also walks naturally.

local PathfindingService = game:GetService("PathfindingService")

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

char.PrimaryPart:SetNetworkOwner(nil)
local target = nil

local pathParams = {
	AgentRadius = 4,
	AgentHeight = 5.11,
	AgentCanJump = true
}

-- Create the path object
local path = PathfindingService:CreatePath(pathParams)

local waypoints
local currentWaypointIndex
local distance

local maxChasingDistance = 50

local function CheckSight(target)
	local rayCastParams = RaycastParams.new()
	rayCastParams.FilterType = Enum.RaycastFilterType.Blacklist
	rayCastParams.FilterDescendantsInstances = {char}

	local direction = (target.HumanoidRootPart.Position - root.Position).Unit * 200
	local rayCastResult = workspace:Raycast(root.Position,  direction, rayCastParams)

	if rayCastResult then
		if rayCastResult.Instance:IsDescendantOf(target) then
			return true
		end
	else
		return false
	end
end

local function FindTarget()
	local closestDist = 0
	local closestPlayer = nil
	local firstIteration = true

	for i,player in ipairs(game.Players:GetChildren()) do
		if  firstIteration then
			if char and char:FindFirstChild("Humanoid") and player and player.Character and player.Character.Humanoid.Health > 0 then
				closestDist = (player.Character.HumanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude
				closestPlayer = player
				firstIteration = false
			end
		else
			if char and char:FindFirstChild("Humanoid") and player and player.Character and player.Character.Humanoid.Health > 0 then
				if(player.Character.HumanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude < closestDist then
					closestDist = (char.HumanoidRootPart.Position - player.Character.HumanoidRootPart.Position ).Magnitude
					closestPlayer = player
				end
			end
		end
	end

	if  closestPlayer and (char.HumanoidRootPart.Position -  closestPlayer.Character.HumanoidRootPart.Position).Magnitude < 100 then
		return  closestPlayer.Character
	else
		return nil
	end
end

local function followPath(targetPivot)
	path:ComputeAsync(char.HumanoidRootPart.Position, targetPivot.Position)
	waypoints = {}

	if path.Status == Enum.PathStatus.Success then
		waypoints = path:GetWaypoints()
		currentWaypointIndex = 1
		hum:MoveTo(waypoints[currentWaypointIndex].Position)
	else
		-- Error (path not found); stop humanoid
		hum:MoveTo(char.HumanoidRootPart.Position)
	end
end

local function onWaypointReached(reached)
	if reached and currentWaypointIndex < #waypoints then
		currentWaypointIndex = currentWaypointIndex + 1
		
		if waypoints[currentWaypointIndex].Action == Enum.PathWaypointAction.Jump then
			hum.Jump = true
		else
			hum:MoveTo(waypoints[currentWaypointIndex].Position)
		end
	end
end

local currentPivot
local lastPivot
local setPivot = false

local function onPathBlocked(blockedWaypointIndex)
	-- Check if the obstacle is further down the path
	if blockedWaypointIndex > currentWaypointIndex then
		-- Call function to re-compute the path
		followPath(lastPivot)
	end
end

local runService = game:GetService("RunService")

wait(5)

path.Blocked:Connect(onPathBlocked)

hum.MoveToFinished:Connect(onWaypointReached)

runService.Heartbeat:Connect(function()
	
	if not target then
		target = FindTarget()
	else
		distance = (target.HumanoidRootPart.Position - root.Position).Magnitude
		
		currentPivot = target:GetPivot()

		if CheckSight(target) then
			hum:MoveTo(target.HumanoidRootPart.Position)
		else
			if setPivot == false then
				setPivot = true
				lastPivot = currentPivot
				followPath(lastPivot)
			else
				local pivotDistance = (lastPivot.Position - currentPivot.Position).Magnitude
				if pivotDistance > 15 then
					setPivot = false
				end
			end
		end
		
		if target.Humanoid.Health < 1 or distance > maxChasingDistance then
			target = nil
		end
	end
end)

The issue you are having is that a waypoint system and following targets is incompatible. If you have a waypoint system, your NPC must stay on the waypoints, the floor is undefined elsewhere.

However, if your NPC leaves it can use the pathfinding service to find it’s way back to the nearest waypoint or path between waypoints.

In games like World of Warcraft, NPCs follow waypoints like yours and after chasing a target which goes out of range or dies, they return to exactly where they first left the waypoint system. This way they avoid calculating the nearest point on the waypoint system.

This is an excellent use case for Polaris-Nav. Join up here and I’ll make sure this is easy to do.

1 Like