Why won't this script work?

So I’m making an enemy NPC that finds the nearest player and gets to them while avoiding obstacles and such. I made some code but for some reason it’s not working and the dummy just stands still and doesn’t move at all, theres no prints or errors in the output either.

local pathfindingService = game:GetService("PathfindingService")

local StartingPoint = script.Parent.PrimaryPart
local humanoid = script.Parent.Humanoid
local root = script.Parent.HumanoidRootPart
local smallest, enemy = math.huge, nil
local Path:Path
Path = pathfindingService:CreatePath()

for _, otherCharacter in pairs(workspace:GetChildren()) do
	if otherCharacter:FindFirstChild("HumanoidRootPart") and otherCharacter ~= script.Parent then
		local distance = (root.Position - otherCharacter.HumanoidRootPart.Position).Magnitude

		if distance < 60 and distance < smallest then
			smallest = distance
			enemy = otherCharacter
		end	
	end
end



local function computePath(startGoal:Vector3, endGoal:Vector3)
	local RetriesLeft = 20--20 retries
	local RetryInterval = 3--delay 3 seconds each time it fails


	for i = RetriesLeft, 1, -1 do
		Path:ComputeAsync(startGoal,endGoal)

		if Path.Status == Enum.PathStatus.Success then
			return Path
		else
			warn("Path failed to compute, retrying...")
			task.wait(RetryInterval)
		end
	end
	error("Path failed to compute")--this will run if the loop is finished or all the retries failed
end

local function WalkHumanoid(humanoid:Humanoid, startGoal:Vector3, endGoal:Vector3)
	local Path = computePath(startGoal,endGoal)

	--setting up path properties
	local WayPoints = Path:GetWaypoints()
	local CurrectWayPointIndex = 2 --the point to go after the first point as the first waypoint is just the start position

	--setup connections
	local MoveToFinishConnection:RBXScriptConnection
	local PathBlockedConnection:RBXScriptConnection

	PathBlockedConnection = Path.Blocked:Connect(function(blockedWaypointIndex)
		warn(blockedWaypointIndex,CurrectWayPointIndex)
		if blockedWaypointIndex >= CurrectWayPointIndex then--a waypoint ahead of us is blocked! recompute the path
			--disconnect the events
			MoveToFinishConnection:Disconnect()
			PathBlockedConnection:Disconnect()

			humanoid.WalkToPoint = StartingPoint.Position --make our humanoid stop walking at its current position and cancel it's movement to the next waypoint
			WalkHumanoid(humanoid,StartingPoint.Position,endGoal)

		end
	end)

	MoveToFinishConnection = humanoid.MoveToFinished:Connect(function(reached)
		if reached then--if the humanoid reach the waypoint within 8 seconds
			if CurrectWayPointIndex < #WayPoints then--if we have not cycle through the last waypoint
				CurrectWayPointIndex += 1--we increase the index by 1 so that the humanoid moves to the next waypoint
				humanoid:MoveTo(WayPoints[CurrectWayPointIndex].Position)--and the same thing
				if WayPoints[CurrectWayPointIndex].Action == Enum.PathWaypointAction.Jump then
					humanoid.Jump = true
				end
			else
				print("Path reached!")
				MoveToFinishConnection:Disconnect()--disconnect the unnecessary event to avoid memory leaks
			end
		else--failed to reach waypoint within 8 seconds, the dummy probably is stuck, so let's recompute it!

			MoveToFinishConnection:Disconnect()
			WalkHumanoid(humanoid,StartingPoint.Position,endGoal)--we are changing our start goal and replacing it to the current position of our dummy instead of reusing the old one

		end
	end)

	--Visualize waypoints
	for _, point:PathWaypoint in ipairs(WayPoints) do
		local part = Instance.new("Part")
		part.Anchored = true
		part.CanCollide = false
		part.Material = Enum.Material.Neon
		part.Color = Color3.fromRGB(255,255,255)
		part.Position = point.Position--PathWaypoint objects have a property named Position which describes their position in the form of Vector3.
		part.Parent = workspace
	end
	-- make humanoid walk
	humanoid:MoveTo(WayPoints[CurrectWayPointIndex].Position)
	if WayPoints[CurrectWayPointIndex].Action == Enum.PathWaypointAction.Jump then
		humanoid.Jump = true
	end
end

WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position)
2 Likes

Can you set all instances inside the npc You put Anchored to false

Everything was already unachored

The script only checks the players once. The NPC will not follow, even if a character spawns later. Instead, turn the player locating bit into its own function:

function getNearestEnemy()
	local enemyFound = false
	for _, otherCharacter in pairs(workspace:GetChildren()) do
		if otherCharacter:FindFirstChild("HumanoidRootPart") and otherCharacter ~= script.Parent then
			local distance = (root.Position - otherCharacter.HumanoidRootPart.Position).Magnitude

			if distance < 60 and distance < smallest then
				smallest = distance
				enemy = otherCharacter
				enemyFound = true
			end	
		end
	end
	if not enemyFound then enemy = nil end
end

Then, in a loop, run the getNearestEnemy, check for an enemy, and if an enemy is present, run WalkHumanoid:

while true do
	task.wait(1)
	getNearestEnemy()
	
	if enemy then
		WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position)
	end
end

Edit: Added “enemy found” confirmation

1 Like

Hey, thanks for your help but for some reason theres an issue, it goes to you but then randomly stops and takes a while to start following you again, heres some footage:

I guess this is the solution??

Where should I type that though as I already disconnected it in the block scenario

Just put WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position) after

MoveToFinishConnection:Disconnect()

Ok so the MoveToFinishConnection variable is only in the HumanoidMove function so it’s unknown, should I just move it to the start or will that maybe affect something?

so remove MoveToFinishConnection:Disconnect() and just put WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position) after print(“Path reached!”)

It still seems to have the same problem and just crashed my game for some reason, this is my code incase I didn’t do your edit properly:

	local pathfindingService = game:GetService("PathfindingService")

	local StartingPoint = script.Parent.PrimaryPart
	local humanoid = script.Parent.Humanoid
	local root = script.Parent.HumanoidRootPart
	local smallest, enemy = math.huge, nil
	local Path:Path
Path = pathfindingService:CreatePath()


function getNearestEnemy()
	local enemyFound = false
	for _, otherCharacter in pairs(workspace:GetChildren()) do
		if otherCharacter:FindFirstChild("HumanoidRootPart") and otherCharacter ~= script.Parent then
			local distance = (root.Position - otherCharacter.HumanoidRootPart.Position).Magnitude

			if distance < 60 and distance < smallest then
				smallest = distance
				enemy = otherCharacter
				enemyFound = true
			end	
		end
	end
	if not enemyFound then enemy = nil end
end



	local function computePath(startGoal:Vector3, endGoal:Vector3)
		local RetriesLeft = 20--20 retries
		local RetryInterval = 3--delay 3 seconds each time it fails


		for i = RetriesLeft, 1, -1 do
			Path:ComputeAsync(startGoal,endGoal)

			if Path.Status == Enum.PathStatus.Success then
				return Path
			else
				warn("Path failed to compute, retrying...")
				task.wait(RetryInterval)
			end
		end
		error("Path failed to compute")--this will run if the loop is finished or all the retries failed
	end

	local function WalkHumanoid(humanoid:Humanoid, startGoal:Vector3, endGoal:Vector3)
		local Path = computePath(startGoal,endGoal)

		--setting up path properties
		local WayPoints = Path:GetWaypoints()
		local CurrectWayPointIndex = 2 --the point to go after the first point as the first waypoint is just the start position

		--setup connections
		local MoveToFinishConnection:RBXScriptConnection
		local PathBlockedConnection:RBXScriptConnection

		PathBlockedConnection = Path.Blocked:Connect(function(blockedWaypointIndex)
			warn(blockedWaypointIndex,CurrectWayPointIndex)
			if blockedWaypointIndex >= CurrectWayPointIndex then--a waypoint ahead of us is blocked! recompute the path
				--disconnect the events
				MoveToFinishConnection:Disconnect()
				PathBlockedConnection:Disconnect()

				humanoid.WalkToPoint = StartingPoint.Position --make our humanoid stop walking at its current position and cancel it's movement to the next waypoint
				WalkHumanoid(humanoid,StartingPoint.Position,endGoal)

			end
		end)

		MoveToFinishConnection = humanoid.MoveToFinished:Connect(function(reached)
			if reached then--if the humanoid reach the waypoint within 8 seconds
				if CurrectWayPointIndex < #WayPoints then--if we have not cycle through the last waypoint
					CurrectWayPointIndex += 1--we increase the index by 1 so that the humanoid moves to the next waypoint
					humanoid:MoveTo(WayPoints[CurrectWayPointIndex].Position)--and the same thing
					if WayPoints[CurrectWayPointIndex].Action == Enum.PathWaypointAction.Jump then
						humanoid.Jump = true
					end
				else
					print("Path reached!")
				WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position)
			end
			else--failed to reach waypoint within 8 seconds, the dummy probably is stuck, so let's recompute it!

				MoveToFinishConnection:Disconnect()
				WalkHumanoid(humanoid,StartingPoint.Position,endGoal)--we are changing our start goal and replacing it to the current position of our dummy instead of reusing the old one

			end
		end)

		--Visualize waypoints
		for _, point:PathWaypoint in ipairs(WayPoints) do
			--[[local part = Instance.new("Part")
			part.Anchored = true
			part.CanCollide = false
			part.Material = Enum.Material.Neon
			part.Color = Color3.fromRGB(255,255,255)
			part.Position = point.Position--PathWaypoint objects have a property named Position which describes their position in the form of Vector3.
			part.Parent = workspace--]]
		end
		-- make humanoid walk
		humanoid:MoveTo(WayPoints[CurrectWayPointIndex].Position)
		if WayPoints[CurrectWayPointIndex].Action == Enum.PathWaypointAction.Jump then
			humanoid.Jump = true
		end
end



while true do
	task.wait(1)
	getNearestEnemy()
	
	
	if enemy then
		WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position)
		
	end
end

Can you re-put this line after WalkHumanoid?

MoveToFinishConnection:Disconnect()

I guess if you don’t put this line then there gonna be Thousand of MoveToFinishConnection Running

It’s definitely a lot less laggy but it still kinda just stands still after reaching the player

that the output says " Path failed to compute" when the npc reach the humanoid?

can you replace

error("Path failed to compute")

to

print("Path failed to compute")

to see the reaction

Oh, looks like there is an error now, it says:
attempt to index nil with ‘HumanoidRootPart’ on line 81

WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position)

oh and it does not print Path Failed to Compute

try this code:

local pathfindingService = game:GetService("PathfindingService")

local StartingPoint = script.Parent.PrimaryPart
local humanoid = script.Parent.Humanoid
local root = script.Parent.HumanoidRootPart
local smallest, enemy = math.huge, nil
local Path:Path
Path = pathfindingService:CreatePath()


function getNearestEnemy()
	local enemyFound = false
	for _, otherCharacter in pairs(workspace:GetChildren()) do
		if otherCharacter:FindFirstChild("HumanoidRootPart") and otherCharacter ~= script.Parent then
			local distance = (root.Position - otherCharacter.HumanoidRootPart.Position).Magnitude
			if distance < 60 and distance < smallest then
				smallest = distance
				enemy = otherCharacter
				enemyFound = true
			end	
		end
	end
	if not enemyFound then enemy = nil end
end



local function computePath(startGoal:Vector3, endGoal:Vector3)
	local RetriesLeft = 20--20 retries
	local RetryInterval = 3--delay 3 seconds each time it fails


	for i = RetriesLeft, 1, -1 do
		Path:ComputeAsync(startGoal,endGoal)

		if Path.Status == Enum.PathStatus.Success then
			return Path
		else
			warn("Path failed to compute, retrying...")
			task.wait(RetryInterval)
		end
	end
	print("Path failed to compute")--this will run if the loop is finished or all the retries failed
end

local function WalkHumanoid(humanoid:Humanoid, startGoal:Vector3, endGoal:Vector3)
	local Path = computePath(startGoal,endGoal)

	--setting up path properties
	local WayPoints = Path:GetWaypoints()
	local CurrectWayPointIndex = 2 --the point to go after the first point as the first waypoint is just the start position

	--setup connections
	local MoveToFinishConnection:RBXScriptConnection
	local PathBlockedConnection:RBXScriptConnection

	PathBlockedConnection = Path.Blocked:Connect(function(blockedWaypointIndex)
		warn(blockedWaypointIndex,CurrectWayPointIndex)
		if blockedWaypointIndex >= CurrectWayPointIndex then--a waypoint ahead of us is blocked! recompute the path
			--disconnect the events
			MoveToFinishConnection:Disconnect()
			PathBlockedConnection:Disconnect()

			humanoid.WalkToPoint = StartingPoint.Position --make our humanoid stop walking at its current position and cancel it's movement to the next waypoint
			WalkHumanoid(humanoid,StartingPoint.Position,endGoal)

		end
	end)

	MoveToFinishConnection = humanoid.MoveToFinished:Connect(function(reached)
		if reached then--if the humanoid reach the waypoint within 8 seconds
			if CurrectWayPointIndex < #WayPoints then--if we have not cycle through the last waypoint
				CurrectWayPointIndex += 1--we increase the index by 1 so that the humanoid moves to the next waypoint
				humanoid:MoveTo(WayPoints[CurrectWayPointIndex].Position)--and the same thing
				if WayPoints[CurrectWayPointIndex].Action == Enum.PathWaypointAction.Jump then
					humanoid.Jump = true
				end
			else
				print("Path reached!")
				MoveToFinishConnection:Disconnect()
				getNearestEnemy()
			end
		else--failed to reach waypoint within 8 seconds, the dummy probably is stuck, so let's recompute it!

			MoveToFinishConnection:Disconnect()
			WalkHumanoid(humanoid,StartingPoint.Position,endGoal)--we are changing our start goal and replacing it to the current position of our dummy instead of reusing the old one

		end
	end)

	--Visualize waypoints
	for _, point:PathWaypoint in ipairs(WayPoints) do
			--[[local part = Instance.new("Part")
			part.Anchored = true
			part.CanCollide = false
			part.Material = Enum.Material.Neon
			part.Color = Color3.fromRGB(255,255,255)
			part.Position = point.Position--PathWaypoint objects have a property named Position which describes their position in the form of Vector3.
			part.Parent = workspace--]]
	end
	-- make humanoid walk
	humanoid:MoveTo(WayPoints[CurrectWayPointIndex].Position)
	if WayPoints[CurrectWayPointIndex].Action == Enum.PathWaypointAction.Jump then
		humanoid.Jump = true
	end
end



while true do
	task.wait(0.5)
	getNearestEnemy()
	if enemy then
		WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position)
	end
end

Hm, it still has the same issue but now I see it prints “Path reached!” multiple times after reaching me and then it just starts bugging and stops, only to kinda twitch every now and then.

Can you test this script ( this is a test):

local pathfindingService = game:GetService("PathfindingService")

local StartingPoint = script.Parent.PrimaryPart
local humanoid = script.Parent.Humanoid
local root = script.Parent.HumanoidRootPart
local smallest, enemy = math.huge, nil
local Path:Path
Path = pathfindingService:CreatePath()


function getNearestEnemy()
	smallest = math.huge --Reset Smallest
	local enemyFound = false
	for _, otherCharacter in pairs(workspace:GetChildren()) do
		if otherCharacter:FindFirstChild("HumanoidRootPart") and otherCharacter ~= script.Parent then
			local distance = (root.Position - otherCharacter.HumanoidRootPart.Position).Magnitude
			if distance < 60 and distance < smallest then
				smallest = distance
				enemy = otherCharacter
				enemyFound = true
			end	
		end
	end
	if not enemyFound then enemy = nil end
end

local function WalkHumanoid(humanoid:Humanoid, startGoal:Vector3, endGoal:Vector3)
	repeat Path:ComputeAsync(startGoal,endGoal) until Path.Status == Enum.PathStatus.Success

	local WayPoints = Path:GetWaypoints()
	local CurrectWayPointIndex = 2 --the point to go after the first point as the first waypoint is just the start position

	local MoveToFinishConnection:RBXScriptConnection
	local PathBlockedConnection:RBXScriptConnection

	PathBlockedConnection = Path.Blocked:Connect(function(blockedWaypointIndex)
		warn(blockedWaypointIndex,CurrectWayPointIndex)
		if blockedWaypointIndex >= CurrectWayPointIndex then--a waypoint ahead of us is blocked! recompute the path
			--disconnect the events
			MoveToFinishConnection:Disconnect()
			PathBlockedConnection:Disconnect()

			humanoid.WalkToPoint = StartingPoint.Position --make our humanoid stop walking at its current position and cancel it's movement to the next waypoint
			WalkHumanoid(humanoid,StartingPoint.Position,endGoal)

		end
	end)

	MoveToFinishConnection = humanoid.MoveToFinished:Connect(function(reached)
		if reached then--if the humanoid reach the waypoint within 8 seconds
			if CurrectWayPointIndex < #WayPoints then--if we have not cycle through the last waypoint
				CurrectWayPointIndex += 1--we increase the index by 1 so that the humanoid moves to the next waypoint
				humanoid:MoveTo(WayPoints[CurrectWayPointIndex].Position)--and the same thing
				if WayPoints[CurrectWayPointIndex].Action == Enum.PathWaypointAction.Jump then
					humanoid.Jump = true
				end
			else
				print("Path reached!")
				MoveToFinishConnection:Disconnect()
				PathBlockedConnection:Disconnect()
			end
		else--failed to reach waypoint within 8 seconds, the dummy probably is stuck, so let's recompute it!

			MoveToFinishConnection:Disconnect()
			PathBlockedConnection:Disconnect()
		end
	end)
	humanoid:MoveTo(WayPoints[CurrectWayPointIndex].Position)
	if WayPoints[CurrectWayPointIndex].Action == Enum.PathWaypointAction.Jump then
		humanoid.Jump = true
	end
end

while true do
	task.wait(0.5)
	getNearestEnemy()
	if enemy then
		WalkHumanoid(humanoid, StartingPoint.Position, enemy.HumanoidRootPart.Position)
	end
end
1 Like

Oh wow! That worked well now the only problem is that it’s kinda choppy but I can just make a new topic for that. Thank you so much man for helping me this whole time.