SimplePath - Pathfinding Module

How do I make npc using simple path to face moving character while moving. I tried every method it all glitches out. All done on server.

you can change the cframe every frame, or use align orientation

How would I make the NPC rotate to a specific orientation (or the rotation the targetpart is facing) after movetofinished?

This module is broken I believe, it either just crashes studio or just gives me an error whenever I attempt to use it, it worked completely fine like a week or two ago.

User error more than likely, show code

2 Likes

I’m not showing the entire script but I will show the parts that use the module, there is more stuff but that mainly just handles damaging and detecting when the player can be attacked. Also, it won’t crash when I remove everything involving the module, so its not the other stuff crashing it.

 local simplePath = require(game.ServerStorage:WaitForChild("SimplePath"))

local SCP = script.Parent
local path = simplePath.new(SCP)

local function findTarget(pos) -- forgot to include at first, had to add it into this message rq.
	local list = game.Workspace:GetChildren()
	local root = nil
	local dist = 500
	local temp = nil
	local human = nil
	local temp2 = nil
	for x = 1, #list do
		temp2 = list[x]
		if (temp2.className == "Model") and (temp2 ~= script.Parent) and not temp2:FindFirstChild("Ignore") then
			temp = temp2:findFirstChild("HumanoidRootPart")
			human = temp2:FindFirstChildOfClass("Humanoid")
			if (temp ~= nil) and (human ~= nil) and (human.Health > 0) then
				if (temp.Position - pos).magnitude < dist then
					root = temp
					dist = (temp.Position - pos).magnitude
				end
			end
		end
	end
	return root
end

task.spawn(function()
	while task.wait() do
		local target = findTarget(SCP.PrimaryPart.Position) -- using my own find plr function instead of simplepath's due to me having to check if its a valid target and not another SCP.
		if target then
			path:Run(target)
		end
	end
end)

I just tested the NPC again today and it works completely fine again, I don’t know why it was bugged yesterday causing the crash, strange…

Hello! I’m lazy but at the beginning of the topic you left a guide on how to implement this module, but I saw that the module has several updates ; Should I use the guide you left at the beginning? I really need to implement it in my game.

How does this module handle multiple NPCs running at once? Haven’t tried it out yet but I plan to make a wave based enemy system with a multiple enemies (5 - 25) and I was curious how this module will hold up when it comes to performance.

If you really need to implement it, don’t be lazy. Read the documentation :smiley:

Im having problems with multiple enemies using pathfinding, they’re moving towards the closest player but they keep walking back and then move a little towards the player and repeating. I’m writing it exactly like the documentation I don’t understand why its doing this. Btw it only does this when multiple enemies are spawned in but if there is only one it works perfectly.

--Import the module so you can start using it
local ServerStorage = game:GetService("ServerStorage")
local SimplePath = require(ServerStorage.SimplePath)

--Define npc
local dummy = script.Parent

--Create a new Path using the Dummy
local Path = SimplePath.new(dummy)

--Helps to visualize the path
Path.Visualize = false

local isDestroyed = false

local maxDistance = 100000

function getClosestPlayer()
	local nearestPlayer, nearestDistance
	for _, player in pairs(game.Players:GetPlayers()) do
		local character = player.Character
		local distance = player:DistanceFromCharacter(dummy.HumanoidRootPart.Position)
		if not character or 
			distance > maxDistance or
			(nearestDistance and distance >= nearestDistance)
		then
			continue
		end
		nearestDistance = distance
		nearestPlayer = player
	end
	if nearestPlayer.Character.HumanoidRootPart then
		return nearestPlayer.Character.HumanoidRootPart
	end
	
end

local run = script:WaitForChild("run")
local humanoid = dummy:WaitForChild("Humanoid")
local runAnimation = humanoid:LoadAnimation(run)


while true do
Path:Run(getClosestPlayer())
end
1 Like

Having an issue where when the Humanoid dies, the rig has a chance to disappear. I have added a ragdoll on death but when removing it still does the same thing.

Has worked well for my game with bot waves, but your surrounding logic needs to minimize how often it tries to recalc routes if things get stuck, etc

Edit: I should note that I manage all the npc from a single script and run their individual logic on a coroutine thread

I manage it from a single script as well. Do you mind sharing how I could optimize recalculating paths when it comes to enemies in waves, as that’s what I’m going for as well?

Keep in my mind, the NPC’s only disappear rarely when normally walking, but almost 80% of the time on death. Not sure if this has to do anything with recalc routes but I’m fairly new to this module.


function class:Start()
	local rig : Model = self.Rig
	local humanoid : Humanoid = self.Rig.Humanoid
	local radius = 20
	local player = nil
	local isFollowing = false
	local followRange = 80 -- Adjust the follow range as needed
	local nearestPlayer

	local Path = simplePath.new(rig)

	Path.Visualize = true

	self.Walk:Play()
	
	humanoid.StateChanged:Connect(function(old, new)
		if new == Enum.HumanoidStateType.Dead then
			self.Idle:Stop()
			self.Walk:Stop()
			Path:Destroy()
		end
	end)
	
	local function followPlayer()
		if rig.Humanoid.Health > 0 then
			nearestPlayer = findNearestPlayer(followRange, rig.PrimaryPart)

			if nearestPlayer then
				isFollowing = true

				--Cancel Current Path
				if Path.StatusType.Active == true then
					Path:Stop()
				end

				Path:Run(nearestPlayer.PrimaryPart.Position)
			else
				isFollowing = false
			end
		end
	end
	
	local function Wander()
		--Cancel Current Path
		if Path.StatusType.Active == true then
			Path:Stop()
		end
		
		self.Walk:Stop()
		self.Idle:Play()
		wait(math.random(2, 5))
		self.Idle:Stop()
		self.Walk:Play()
		Path:Run(rig.PrimaryPart.Position + Vector3.new(math.random(-radius, radius), 0 , math.random(-radius, radius)))
	end

	-- Check for nearby players periodically
	task.spawn(function()
		while true do
			if not isFollowing then
				followPlayer()
			end
			wait() \
		end
	end)

	-- Wandering
	Path.Reached:Connect(function()
		if not isFollowing then
			Wander()
		else
			if not self.Walk.IsPlaying then
				self.Idle:Stop()
				self.Walk:Play()
			end
			
			Path:Run(nearestPlayer.PrimaryPart.Position)
		end
	end)
	
	Path.WaypointReached:Connect(function()
		if isFollowing then
			if not self.Walk.IsPlaying then
				self.Idle:Stop()
				self.Walk:Play()
			end
			
			Path:Run(nearestPlayer.PrimaryPart.Position)
		end
	end)
	
	Path.Error:Connect(function(errorType)
	--	warn(errorType)
		if isFollowing then
			if not self.Walk.IsPlaying then
				self.Idle:Stop()
				self.Walk:Play()
			end
			
			Path:Run(nearestPlayer.PrimaryPart.Position)
		else
			Wander()
		end
	end)
	
	--Dummy knows to compute path again if something blocks the path
	Path.Blocked:Connect(function()
		if isFollowing then
			if not self.Walk.IsPlaying then
				self.Idle:Stop()
				self.Walk:Play()
			end
			
			Path:Run(nearestPlayer.PrimaryPart.Position)
		else
			if Path.StatusType.Active == true then
				Path:Stop()
			end	
			
			Path:Run(rig.PrimaryPart.Position + Vector3.new(math.random(-radius, radius), 0, math.random(-radius, radius)))
		end
	end)

	-- Start wandering initially
	Path:Run(rig.PrimaryPart.Position + Vector3.new(math.random(-radius, radius), 0, math.random(-radius, radius)))
end

Here is my currently enemy code, not sure how I’m able to fully optimize it but help would be apprecitaed

2 Likes

please help, wth do i do when this happens?
ServerStorage.SimplePath:327: attempt to perform arithmetic (sub) on number and nil

Same here. I set the NPC’s WalkSpeed to 18 and it causes the NPC to stutter which makes it slower. Have you found a solution yet?

I dont know why, but after Path:Destroy() (i think), its still gives an error like:

attempt to index nil with 'Status'
1 Like

Also experiencing this issue. It seems to occur once you’ve destroyed a path once and now want to create a new one.

When I have multiple npcs, and I use Path:Destroy(), it says attempt to call missing method Destroy() on table

Really good module! Easy to use, and working perfectly!