SimplePath - Pathfinding Module

Hi,

I am moving an NPC to multiple points but it will only work if the Parts have can collide enabled.
What am I doing wrong?

--Import the module so you can start using it

local ServerStorage = game:GetService("ServerStorage")

local SimplePath = require(ServerStorage.SimplePath)

local Nextpart = 1

local partArry = {}

for i, child in ipairs(game.Workspace.Parts:GetChildren()) do

partArry[i] = child

end

--Define npc

local Dummy = workspace.CanterCrow

-- Define a part called "Goal"

local Goal = workspace.Parts[tostring(partArry[1])]

--Create a new Path using the Dummy

local Path = SimplePath.new(Dummy)

--Helps to visualize the path

Path.Visualize = true

--Compute a new path every time the Dummy reaches the goal part

Path.Reached:Connect(function()

Nextpart = Nextpart + 1

if Nextpart <= 3 then

Goal = workspace.Parts[tostring(partArry[Nextpart])]

end

end)

--Dummy knows to compute path again if something blocks the path

Path.Blocked:Connect(function()

Path:Run(Goal)

end)

--If the position of Goal changes at the next waypoint, compute path again

Path.WaypointReached:Connect(function()

Path:Run(Goal)

end)

--Dummmy knows to compute path again if an error occurs

Path.Error:Connect(function(errorType)

Path:Run(Goal)

end)

Path:Run(Goal)

Using the code you provided, I was able to identify a bug. It is now fixed in v2.2.0.

To do pathfinding to a group of parts, you can do something like the following:

local dummy = workspace.Dummy
local SimplePath = require(game:GetService("ServerStorage").SimplePath)
local Path = SimplePath.new(dummy)
Path.Visualize = true

--3 parts in workspace called: "1", "2", "3"
local goal = workspace["1"]

Path.Error:Connect(function() Path:Run(goal) end)
Path.WaypointReached:Connect(function() Path:Run(goal) end)
Path.Blocked:Connect(function() Path:Run(goal) end)

Path.Reached:Connect(function()
	goal = workspace:FindFirstChild(string.format("%i", tonumber(goal.Name) + 1)) or workspace["1"]
	Path:Run(goal)
end)

Path:Run(goal)

robloxapp-20220129-2058251

1 Like

v2.2.0 (2022-01-29)

  • Fixed bug with Path.WaypointReached
    • Emerges if Path:Run() is binded to both Path.WaypointReached and Path.Reached for a Humanoid
    • Before the final waypoint, Path.WaypointReached is fired and immediately after Humanoid.MoveTo (then Path.Reached) gets fired almost simultaneously
    • Path.Run executed within the scope of Path.WaypointReached to finish after Path.Reached gets fired
    • Fix: Path.WaypointReached will not fire for the second-last waypoint for Humanoid agents
  • Added feature to configure settings for individual agents
    • Overridden settings can be passed as a dictionary to the constructor
  • Added a new setting: Settings.JUMP_WHEN_STUCK
    • By default, the agent attempts to jump if it gets stuck in the same position for Settings.COMPARISON_CHECKS + 1
    • This can now be disabled and can be handled as needed
  • Added a new ErrorType: SimplePath.ErrorType.AgentStuck
    • Use this ErrorType to define new behavior when Settings.JUMP_WHEN_STUCK is set to false
  • Minor edits to the documentation

I’m not sure why it does this, but whenever I go near this cement beam, the zombies try to compute a path which goes on it and then down to me. They get stuck for some reason. Their JumpWhenStuck is true, I don’t get why this happens.

That’s very odd behaviour, would you mind showing your implementation?

local SimplePath = require(script.SimplePath)

function findNearestTorso(pos)
	local list = game.Workspace:children()
	local torso = nil
	local dist = 10000
	local temp = nil
	local human = nil
	local temp2 = nil
	local player = false
	for x = 1, #list do
		temp2 = list[x]
		if (temp2.className == "Model") and (temp2 ~= script.Parent) then
			temp = temp2:findFirstChild("HumanoidRootPart")
			human = temp2:findFirstChild("Humanoid")
			if (temp ~= nil) and (human ~= nil) and (human.Health > 0) then
				if game.Players:GetPlayerFromCharacter(temp2) and (temp.Position - pos).magnitude < dist then
					torso = temp
					dist = (temp.Position - pos).magnitude
					print(temp.Parent)
				end
			end
		end
	end
	return torso
end

local Dummy = script.Parent
local Goal = Vector3.new()
local Path = SimplePath.new(Dummy)

Path.Visualize = false

Path.Blocked:Connect(function()
	Path:Run(Goal)
end)

Path.WaypointReached:Connect(function()
	Path:Run(Goal)
end)

Path.Error:Connect(function(errorType)
	Path:Run(Goal)
end)

while true do
	wait(.2)
	local target = findNearestTorso(script.Parent.HumanoidRootPart.Position)
	if target then
		Goal = target.Position	

		Path:Run(Goal)

	end
end

Here It Is!

Edit: They only get stuck when near the wall. I don’t know what exactly it is, but it only happens on parts that are near their HipHeight, 2 is the default value for HipHeight. I think it’s something to do with that.

On flat lands though, works perfectly, they don’t stutter, they don’t take time to change paths, everything works fine.

the above code also results in

image

Try increasing the COMPARISON_CHECKS setting or try to increase the waypoint spacing and see if that makes any difference.

increased Comparison checks to

  • 5 (no change)
  • 10 (no change)
  • 20 (no change) :frowning:

Documentation for ErrorType of ComputeError are wrong. Should be ComputationError. It gave me a nil when it happened and had to look on the simple path module.
image

Thanks for letting me know. The mistake is now fixed in the documentation.

1 Like

Hi, 1st of all, thank you for the module! is awesome :slight_smile:

I am running two functions, 1 to generate a random walking pattern and the 2nd is to go after the target. I am unable to end the 2nd function with Path:Stop() after the target is too far so now the NPC is walking random and trying to go at the target direction causing it to move all weird.


	if targetPresent == false then 
		print("walkrandom no target")
		local PathRandom = SimplePath.new(worm)
		local goal = myRoot.Position + Vector3.new(math.random(-0,10),0,math.random(-0,50))		
		PathRandom.Visualize = false
		PathRandom:Run(goal)

		if targetPresent == true then 
			print("walkrandom OFF")
			PathRandom:Stop()
		end
	end
end

function findPath(target)

	
		local Path = SimplePath.new(worm)
		Path.Visualize = false
		Path.Blocked:Connect(function()
			Path:Run(target)
		end)
		Path.WaypointReached:Connect(function()
			Path:Run(target)
		end)
		Path.Error:Connect(function(errorType)
			Path:Run(target)
		end)
		Path:Run(target)

		if (myRoot.Position - target.Position).Magnitude <= 20 then
			myHuman.WalkSpeed = 50
			local Sound = Instance.new("Sound", target.Parent.HumanoidRootPart)
			Sound.SoundId = "rbxassetid://6028987187"
			Sound:Play()
			task.wait(1)
			target.Parent.Humanoid:takeDamage(200)
		elseif(myRoot.Position - target.Position).Magnitude > 200 then
			Path:Stop(target)
--			Path:Destroy(target)
		targetPresent = false
		myHuman.WalkSpeed = 8
			print("target too far")
		end
	
end```

I tried Parth:Stop() and also tried Path:Destroy() and both and none and all everything in between... with no luck.

Make sure you only call Path:Stop() on the same Path object. I can’t say for sure without going over your entire script but looking at your functions, I can see that you create a new Path inside of both and you also call Path.Stop in both as well. I would suggest you instantiate Path only once per rig for the entire system to keep everything in order and prevent unintentional behaviour.

I’m also having a slightly similar problem… when I put the goal on top of the wall then move it somewhere else, it thinks that the top of the wall is a passable part for some reason (I’m using a skinned mesh + non-edited code from the API) https://gyazo.com/aab3690297f757c2a5cc0b9abaf680b4

1 Like

Something weird is happening.
When using a R6 rig (from the default rig maker) the NPC will hop constantly to the target.
Can anyone re-create this using the example map?
Help me fix this please.

1 Like

When I go into the actual module script and comment the 2 single lines that say “Humanoid.Jump = true” the R6 rig stops hopping towards the target. What could cause this?

Edit: this line causes it (line 66)

		if (p1.Y - p0.Y  >= self._humanoid.HipHeight) or (raycast and p1.Y - raycast.Position.Y  >= self._humanoid.HipHeight) then
		--	self._humanoid.Jump = true
		end

I understand it’s something with the HipHeight of 0, as you mentioned you fixed it once but still seems to be occuring xD

1 Like

You are using an older version of the module. Try it again by adding in the newer version using the Roblox link.

1 Like

xD, that’s true… seems like I used the one from the example map that you still have uploaded, you may wanna change it later. thanks :innocent:

I have another question since you answered so fast…
I get my characters position and I want the NPC to stop near the player for an attack animation.
But since it’s a while loop it loops through Path:Stop() and fires a warning
SimplePath: Attempt to run Path:Stop() in idle state

		local function GetNearestPlayer(minimumDistance)
			local closestMagnitude = minimumDistance or math.huge
			local closestPlayer
			for i,v in next, game.Players:GetPlayers() do
				local chr = v.Character
				if (chr) then
					if (model.Humanoid.Health > 0) then
						local distance = v:DistanceFromCharacter(model.HumanoidRootPart.Position)
						local mag = (model.HumanoidRootPart.Position - model.HumanoidRootPart.Position).Magnitude
						if distance <= closestMagnitude and distance > stat[2] then
							closestPlayer = v
							closestMagnitude = mag
							print('running')
							Path:Run(chr:WaitForChild("HumanoidRootPart"))
							--runAnimation:Play()
						end
						if distance < stat[2] then
							print('attacking')
							Path:Stop()
							--runAnimation:Stop()
						end
					end
				end
			end
			return closestPlayer
		end
		
		coroutine.wrap(function()
			while true do
				local target = GetNearestPlayer(stat[1])
				wait()
			end
		end)()

Any idea on how to properly use the Path:Stop()?

If Path.Visualize = true and you call Path:Destroy() it creates a C stack overflow in the output.