SimplePath - Pathfinding Module

I’m newer to scripting and understand a bit about the client v. server distinction and remote events, but I’m unsure how I would do pathfinding locally and “give ownership of the humanoid to the client”. I tried moving most everything to a local script and created the path in a while loop in that script and fired a remote event to the server which had the humanoid run to that point, but when I tested it, the AI stood there and didn’t do anything and there were no errors. Maybe I just need to tinker around with that particular strategy more.

I’m not against the larger humanoid jumping, I just want it to stick to attempting jumps that are actually possible for it to complete. It doesn’t seem to grasp its own jump power or walking speed and behaves like they’re both default. I did try setting AgentCanJump to false and I got entirely new bugs where the bot would stop dead in its tracks when I jump up the stairs and wouldn’t start moving again (even if I jumped down) unless I got really close to it. That might be an issue with PathfindingService in general, though, since I noticed that bug when trying to essentially recreate this module before I knew it existed.

1 Like

For the speed problem just do a Magnitude check on the waypoint. If there or close-enough; exit the loop and move on to the next. And always aim a bit beyond the next actual waypoint…

Remember when doing a .magnitude check, to strip the Y value from the subtraction by making new Vector3s with the .y value set to 0 as the height of the waypoint may not be the same height, as the Torso, or whatever Part you are subtracting it from. I guess; MoveToFinished could run into this problem too, so set the .y value to the same height as torso of NPC will be, once it gets there, for the input into the MoveToFinished (I don’t know how that Method works…); Or re-align your walls onto the exact voxels of the waypoints (See; bottom)

For the Jump problem, if it’s hard-wired somehow, measure the height of the next node yourself against the Torso height (Only the .y value). If too high;
abandon the path.
Do another Path Find but change the input to pathfind, of the NPC’s .y value lower, like to his feet, so that that the jump is definitely out of range, and the Path tries some other route.

Also you must check the erros returned by pathfind.

The two most important erros are Bad Origin (NPC is too close to some part, so that the Voxel Chunk that it thinks it’s starting from is actually 4 studs over…) so move the Input of the Origin to different places (You can spiral out to different points, by multiples of 4 studs, until you don’t get the error anymore (An empty spot shouldn’t be too far away)).

And Bad Destination Error (I forget the actual error names). Target is inside of a Part or there is no such path there (Two different errors I think)

I just move the target input Vecto3 closer to the NPC, Re-call Pathfind, until you stop getting the Error but maybe you can come-up with something better… Go nuts…

If you never stop getting the errors and have reached the end of your remedies; you are going to have to abandon pathfind, and do something else: Switch to a ray-cast (“Wall-hugger”) AI routine for a second or two; or move some random direction while jumping, or just go straight at player for a second; or some other brilliant idea… If you make some prgress; then try a Pathfind from the new spot… Use your imagination…

You can’t just do nothing…, or maybe you can… Have the NPC start a campfire and warm his hands for a few seconds, or something…

Also, only go to the first 2 or 4 waypoints. Then call another pathfind.

If really close to Player; abandon pathfind, and go straight at him…

You should put an additional; Impeded, check just to make sure that Humanoid is getting somewhere; regardless of no errors being returned…, and do something about it…

I also had a time-out on reaching each waypoint in a reasonable amount of time… Remedy: all of the above, or try the next waypoint or…

You can calibrate all of the numbers for different speed AIs, at the beginning of the Script, by testing a known path for a few steps… I always feel that a newly spawned AI should start inside a building, a bush, or grave, or down some steps, where it looks natural for them to be entering from anyway…

[Lining up your walls, doorways, floors, and steps; so that they sit on the exact same Voxel chunks, which the Pathfind is using lessens the Error returns also… You can find the chunks by drawing 4x4x4 parts in the waypoints returned by pathfind which you should always be doing anyway while debugging. Just like you always draw Rays…]

Remember; computers are stupid: You are the brains of the operation. and must think of everything before it happens.

GL

2 Likes

I don’t know if it has been requested before or that it already exists, but I have a feature request: The ability to print what error is happening, currently if anything happens there is no error message, only a non-responding AI, which is, pretty annoying.

But apart from that, it is a great Pathfinding Module!

There are more than one ways to see what type of error occurred.

--Simply use the Path.Error to output errors
Path.Error:Connect(print)

--or

--Path:Run() returns false if error occurs
--Error is then stored as Path.LastError
if not Path:Run() then print(Path.LastError) end
2 Likes

I’m getting a “LimitReached” error, what should I do?

Just compute the path again using Path:Run().

That’s the whole point, that doesn’t work. Below is my full code I’m using right now

local ServerStorage = game:GetService("ServerStorage")
local SimplePath = require(ServerStorage.SimplePath)

local Dummy = workspace.Neighbor

local Goal = workspace.Goal

local Path = SimplePath.new(Dummy)

Path.Visualize = true

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

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

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

Path.Error:Connect(function(errorType)
	Path:Run(Goal)
	print("Pathfinding Error! See below for more details")
	Path.Error:Connect(print)
end)

Path:Run(Goal)

Remember that Path:Run() returns false if pathfinding isn’t possible. Use print(Path:Run()) and see if that returns true or false.

1 Like

Does SimplePath support Pathfinding modifiers?

Of course! As you know, for pathfinding modifiers you just add a key named “Costs” to the agent parameters. It works exactly the same way in SimplePath. Just pass in your agent parameters normally to the constructor.

local Path = SimplePath.new(character, agentParameters)

API Reference

1 Like

Thank You! the reason i asked was because i didnt see the option to do so in the documentation. but i guess i missed it.

I wanna propose a simple new feature
Having to fire an event that I can listen to when comparison checks execute. :smiley:
and that event can be probably retrieved from the path variable same as path:run(), path.Error

Interesting idea. But I’m not sure about the use case of this feature. Let me know how you would implement this in your code.

This is probably just a specific use case scenario but I modified the module to get this feature myself. I just used it as a means to teleport the NPC to a safe place again when comparison checks execute instead of just jumping since I do not plan to allow jumping NPC’s in the game.

There are probably a lot of ways you can use this that I haven’t even come to think of it. Either way the best use case I can think of is just another way to check if NPC is stuck. since Path.Error doesn’t have this feature. :sweat_smile:

That is an interesting idea. The humanoid automatically jumps by default if it gets stuck but sending the error through Path.Error is let the developer handle that part and they can choose to do something else instead. I’ll definitely consider adding this feature in the next update.

2 Likes

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

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?