SimplePath - Pathfinding Module

I did some quick testing on Studio using a scaled-down rig. To stop the slipping for the rig, I enabled CustomPhysicalProperties and bumped up the density of all the limbs to 100 (ref). I then changed the jump setting to make sure the humanoid can jump at a certain distance and found 43 to be an ideal jump power in my case. Inside the pathfinding script, I passed in the agent parameter WaypointSpacing by increasing it to 7. After all of these changes, this was the result:

robloxapp-20220114-1530501.wmv (364.4 KB)

It’s about tweaking various settings until you get what you want. There isn’t just one way to fix it.

I would suggest you do pathfinding on humanoids instead of non-humanoids. Part of the reason why is because, with humanoids, you have more control. For example, you can easily change properties like speed and jump power directly from the humanoid object. For non-humanoids, you would have to create your own logic to move the model including the handling of jumps, etc.

The issues I’m experiencing with the different size/speed humanoids seem to hinge on the pathfinding apparently calculating waypoints as though they were travelling at the normal speed regardless of what their actual speed is. The smaller, faster humanoid jumps further than it needs to because its walk speed is 32 while the path seems to be calculated for normal speed. It also has issues chasing me like in the second video I posted, where it will slow down to normal walk speed even though it’s supposed to be going fast because of how it’s following the waypoints. The “high density” trick worked kinda well for fixing the first problem but made the second problem even worse (and I don’t like the way it made the bot able to easily push around the player character). The larger, slower humanoid seems to have its pathfinding computed the same way, but since it’s slower than normal, the pathfinding thinks that it can make jumps that it actually can’t (and shouldn’t), so you get that jumping in place that you see in the first video I posted.

It may just be that this specific pathfinding module isn’t suited for my purposes, but if you have any other ideas on how to fix this I’m all ears.

You’re absolutely right. The path is computed using the default settings which the Humanoids use. This is also why it’s encouraged to make use of agent parameters when using a custom Humanoid. You can do nothing in this case except tweaking various settings until you arrive at what you’re looking for. This is also a challenge when using PathfindingService on its own without SimplePath.


The second problem where the Humanoid is unable to reach the character despite its speed is a problem that I addressed before privately with another developer. The problem here is due to the delay between the client and server. Since pathfinding, by default, is done on the server, when your character moves, there’s a delay before the server receives your exact position. The fix is simply to give ownership of the Humanoid to the local client it’s chasing and do pathfinding locally. Again, this problem is unrelated to SimplePath. To test this theory out, just try to constantly move a part to your character’s position on the server. Something like this:

local p = Instance.new("Part", workspace)
p.Size = Vector3.new(1, 1, 1)
p.CanCollide = false
p.Anchored = true

while true do
	local c = workspace:FindFirstChild("V3N0M_Z")
	if c then
		p.CFrame = c.PrimaryPart.CFrame
	end
	task.wait()
end

I’m not entirely sure what you mean by this. SimplePath ensures the Humanoid reaches its goal no matter what. It may even jump on top of its goal if that helps it get closer. If you want the Humanoid to stop a certain distance away from the goal, you should include code that performs distance checks and stops pathfinding as soon as the Humanoid reaches a certain distance to the goal.


The agent parameter AgentCanJump can be set to false to disable jump. Again, the pathfinding behavior of a custom Humanoid is completely up to you. The default settings are just there by default.


In the end, these problems are completely independent of SimplePath. However, even if I modify its code in the future to fix some of the problems somehow, fixing them is still out of the scope of this module. I hope my explanations help with the issues you are facing.

Minor Update:

  • Re-did the website with dark colors
  • Repository now has better version control

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