SuperPath (1.2.2)
Creator Store | Download File (14.3 KB) | Donate :3
Note: The module is found to be kinda unstable and is undergoing a full rewrite (v2), possibly featuring cleaner API interfaces, stabler navigation, and less blackboxing.
Ever struggled with Pathfinding or the potential spaghetti code that comes alongside it? SuperPath can be used to Pathfind easily, and provides other additional features that might just come in handy.
Demo here: SuperPath Testing Place
Features
Feature |
SimplePath |
NoobPath |
SuperPath |
|---|---|---|---|
| Lifecycle | Run-Stop | Run-Stop | Run-Pause-Resume-Halt |
| Non-Humanoid Support | |||
| Waypoint Visualization | |||
| Waypoint Optimization | |||
| Rate Limiting | |||
| Autocomplete Support | |||
| Partial Path Friendliness | |||
| Agent Stuttering Protection | |||
| Built-in Raycast Checks | |||
| Runtime AgentParams Changing | |||
| QoL |
SuperPath, distinct from other alternatives, provides additional features like pre-run raycast checks and better waypoint optimization, check below for information!
Details
Lifecycle
Most (or all) of the pathfinding modules on DevForum adopted a Run-Stop lifecycle, it works fine. SuperPath, however, took the Run-Pause-Resume-Halt approach, as described below:
- Run: Computes a new route, and move the Agent along it
- Pause: Saves information of current route, but temporarily stops Agent’s movement. This can be resumed by
:Resume() - Resume: Resumes Agent’s movement after it being paused
- Halt: Clears all information of current route and stops Agent’s movement
You can jump straight from Running to Halting without necessarily Pausing or Resuming, however.
Raycast Checks
It should be noted that this feature is exclusive for humanoids!
In SuperPath, “Raycast Checks” means running a raycast check before pathfinding. Under feasible conditions (determined by raycast results, check below for more info), SuperPath will call Humanoid:MoveTo() without pathfinding in prior for a smooth movement and less computational overhead.
Raycast Checks are, by default, turned off. To enable it, you can do something like this:
local Path = SuperPath.Agent(path.to.rig, agentParams, {
RaycastPreRun = true,
RaycastSettings = {
FilterType = Enum.RaycastFilterType.Exclude,
-->> Both FilteredInstances and FilteredTags accept both an array and object
FilteredInstances = {}, -->> you can omit this if needed
FilteredTags = {}, -->> Same deal. Future instances with these tags are counted in.
MaxYOffset = 1.5, -->> Fallbacks to pathfinding if goal.Y - origin.Y > MaxYOffset
Cooldown = 0 -->> Fallbacks to pathfinding if now - last < Cooldown
}
})
You could also directly modify SuperPath.Config module so settings are made default.
These results allow direct :MoveTo()
RaycastResult.Instancedoes not exist; orRaycastResult.Position:FuzzyEq(goal, 0.2)istrue
Waypoint Optimization
SuperPath offers Waypoint Optimizations as a way to enhance path qualities and remove jumping of agents if unneeded and wished.
SuperPath optimizes path in three ways (where each can be opted-in and out):
- Deleting unnecessary waypoints (too close to previous one and is not special)
- De-jumping minor step up waypoints
- De-jumping fall waypoints
While compact waypoints are prone to deletion, they will always be preserved if the waypoint has either a unique label or is for jumping.
These features are disabled by default. To enable them, you can do something like this:
local Path = SuperPath.Agent(path.to.rig, agentParams, {
DeleteUnneededWaypoints = true, -->> Deletes unneeded waypoints
OnStepupNoJump = true, -->> De-jumps waypoints that need not jumping
OnFallNoJump = true -->> De-jumps waypoints that is assumed for Agent to fall
})
You could also directly modify SuperPath.Config module so settings are made default.
Non-humanoid Usage
SuperPath supports nonhumanoid movement, though it will take more set-up compared to humanoids. You need to write custom movement logic for nonhumanoids (or humanoids if UseHumanoid = true. As SuperPath does not run internal position checks, you will need to notify SuperPath when movement to a certain waypoint/goal has completed with Path:FinishWaypoint().
For this guide, assume a following Agent hierachy:

Note that Part is the primary part of the Agent model. A primary part is required for non-
Example #1 (no raycast checks):
local TS = game:GetService("TweenService")
local part = script.Parent
local SuperPath = require(path.to.SuperPath)
local Path = SuperPath.Agent(part.Parent)
local TI = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.In)
local function toNextWaypoint()
local tween = TS:Create(part, TI, {Position = Path.NextWaypoint.Position})
tween:Play()
tween.Completed:Once(function()
Path:FinishWaypoint() -- Tell SuperPath that waypoint movement has finished (MUST)
end)
end
Path.StartRun:Connect(toNextWaypoint)
Path.WaypointReached:Connect(toNextWaypoint)
Path:Run(Vector3.new(50,2,100))
Example #2 (with raycast checks):
local TS = game:GetService("TweenService")
local SuperPath = require(path.to.SuperPath)
local part = script.Parent
local Path = SuperPath.Agent(part.Parent, nil, {
RaycastPreRun = true,
RaycastSettings = {
FilterType = Enum.RaycastFilterType.Exclude,
FilteredInstances = {workspace.Waypoints, part}
}
})
local TI = TweenInfo.new(2, Enum.EasingStyle.Sine, Enum.EasingDirection.In, 0, false, 0)
Path.StartRun:Connect(function(goal, isDirectRun)
local nextPos = goal
if not isDirectRun then -- If not a direct run, then move to next waypoint
nextPos = Path.NextWaypoint.Position
end
local t = TS:Create(part, TI, {Position = nextPos})
t:Play()
t.Completed:Once(function()
Path:FinishWaypoint() -- Tell SuperPath that waypoint movement has finished
end)
end)
Path.WaypointReached:Connect(function()
local t = TS:Create(part, TI, {Position = Path.NextWaypoint.Position})
t:Play()
t.Completed:Once(function()
Path:FinishWaypoint() -- Tell SuperPath that waypoint movement has finished
end)
end)
Path:Run(Vector3.new(50,2,100))
API Document
Types (Recommended to check this first)
export type ErrorType =
"CompError" |
"RateLimit" |
"AgentStuck" |
"TargetUnreachable" |
"MaxDistExceeded"
export type ToVector = Vector3 | CFrame | Model | BasePart | vector
export type AgentParams = {
AgentRadius: number?,
AgentHeight: number?,
AgentCanJump: boolean?,
AgentCanClimb: boolean?,
Costs: {[string]: number}?,
PathSettings: {SupportPartialPath: boolean}?
}?
export type RaycastSettings = {
FilterType: Enum.RaycastFilterType?,
FilteredTags: {string?} | string?, -->> Also counts in future instances
FilteredInstances: {Instance}? | Instance?, -->> Also counts their descendants
Cooldown: number?, -->> Until cooldown passed can a raycast be performed
MaxYOffset: number?
}?
export type PathSettings = {
RateLimit: number?,
UseHumanoid: boolean?, -->> Whether SuperPath should use Humanoids found in Agent
DoStuckChecks: boolean?,
StuckChecks: number?,
RetryOnBlocked: boolean?,
DeleteUnneededWaypoints: boolean?,
MinimumSpacing: number?,
OnFallNoJump: boolean?,
OnStepupNoJump: boolean?,
RaycastPreRun: boolean?,
RaycastSettings: RaycastSettings?,
Visualize: boolean?,
VisualizerParent: Instance,
}?
Config
return {
RATE_LIMIT = 0.1, -->> How long that is required before doing subsequent :Run() calls
DO_STUCK_CHECKS = true,
STUCK_CHECKS = 1, -->> 1 + STUCK_CHECKS are done before assuming Agent is stuck
RETRY_ON_BLOCKED = false, -->> Whether to recompute path on path blocked
DELETE_UNNEEDED_WAYPOINTS = true, -->> Whether to delete unnecessary waypoints automatically
MINIMUM_SPACING = 1,
ON_FALL_NO_JUMP = false, -->> Whether to disallow Agent to jump when falling
ON_STEPUP_NO_JUMP = false, -->> Whether to disallow Agent to jump on slight elevation
RAYCAST_PRE_RUN = false, -->> Whether should Agent directly move to goal if direct LoS is made
RAYCAST_SETTINGS = {
FilterType = Enum.RaycastFilterType.Exclude,
FilteredTags = {},
FilteredInstances = {},
Cooldown = 0,
MaxYOffset = 2 -->> If Goal - AgentPos > MaxYOffset, raycast won't even run
},
WAYPOINT_TIMEOUT = 8, -->> Maximum time for Agent to move from one waypoint to another
VISUALIZE = false,
VISUALIZER_COLOURS = { -->> Unchangable in PathSettings btw
REGULAR = Color3.new(1, 0.36, 0),
GOAL = Color3.new(0.28, 1, 0.02),
JUMP = Color3.new(0.48, 0.42, 1),
SPECIAL = Color3.new(0, 0.01, 1)
},
VISUALIZER_PARENT = workspace
}
SuperPath
Constructors
.Agent(agent: Model, agentParams: AgentParams?, pathSettings: PathSettings?): Path
"Constructs a Path for the given Agent"
Properties
ErrorTypes = {
CompError = "CompError", -->> Computational error
RateLimit = "RateLimit", -->> Excessive :Run() calls before cooldown passed
MaxDistExceeded = "MaxDistExceeded", -->> Goal - Origin > 3000 studs (Roblox hardcoded limit)
TargetUnreachable = "TargetUnreachable", -->> Waypoint traversal timeout exhausted
AgentStuck = "AgentStuck" -->> Agent deemed stuck after (StuckChecks + 1) checks
}
Path
PROPERTIES
-->> Note that all of these are not necessarily inputted on .Agent() call
WaypontTimeout: number
"Maximum time allowed for Agent to walk to next waypoint"
Visualize: boolean
"Whether to visualize waypoints of route"
VisualizerParent: Instance
"The instance to which visualizer parts are parented, defaults to game.Workspace"
DeleteUnneededWaypoints: boolean
"Whether to delete compact waypoints, see above for more info"
MinimumSpacing: number
"The minimum spacing of waypoints accepted by optimization algorithm, if DeleteUnneededWaypoints is true"
OnFallNoJump: boolean
"Whether to de-jump falling waypoints, see above for more info"
OnStepupNoJump: boolean
"Whether to de-jump minor stepup waypoints, see above for more info"
DoStuckChecks: boolean
"Whether to check if Agent is stuck"
StuckChecks: number
"How many checks to be done before determining Agent is stuck."
"Note that 1 is always added to this value"
RetryOnBlocked: boolean
"Whether to automatically call :Run() on route blockage"
RaycastPreRun: boolean
"Whether to run raycast checks before pathfinding, see above for more info"
RaycastSettings: RaycastSettings
"See above for more info"
RateLimit: number
"Minimum time in seconds before calling :Run() again"
Agent: Model READ ONLY
"The Agent of the Path"
Humanoid: Humanoid? READ ONLY
"Humanoid of the Agent if any"
"If you wish not to use humanoids even if it is inside the Agent, input `UseHumanoid = false`"
"into PathSettings when creating Path"
Path: Path READ ONLY
"Path object created by PathfindingService:CreatePath()"
Idle: boolean READ ONLY
"Whether the Agent is not navigating via the path"
LastError: ErrorType READ ONLY
"The last error the Path has experienced, nil if none exist"
WaypointIndex: number READ ONLY
"The index of current waypoint goal"
NextWaypoint: PathWaypoint READ ONLY
"The current waypoint goal"
LatestWaypoint: PathWaypoint READ ONLY
"The waypoint which the Agent just navigated across"
LastWaypoint: PathWaypoint READ ONLY
"The waypoint before Path.LatestWaypoint"
IsPartial: boolean READ ONLY
"Whether the current route is partial, if supported"
PathWaypoints: {PathWaypoint} READ ONLY
"The waypoints of the current route"
"All waypoints in this table are processed"
Goal: Vector3? READ ONLY
"The current goal, nil if none"
PositionRecords: {Vector3} READ ONLY
"A table of of Agent's position recorded each :Run() call"
"This is alao used internally to detect if Agent is stuck"
AgentInAir: boolean READ ONLY
"Whether the Agent is in air"
"This will always be false if Path.Humanoid does not exist"
IsPathfinding: boolean READ ONLY
"Whether the Agent is navigating with direct :MoveTo() calls or pathfinding"
"This updates each :Run() call"
"Note that this will remain its state even when Path.Idle = true"
IsDestroying: boolean READ ONLY
"This will be true for one heartbeat before the Path is destroyed with :Destroy()"
LastRun: boolean READ ONLY
"Time in seconds (with sub-millisecond precision) since the last :Run() call"
"This is used internally to rate limit :Run() calls"
-- you made it to the end!
Methods
-- for you qol here, ToVector = Vector3 | CFrame | Model | BasePart | vector
Run(goal: ToVector, origin: ToVector?): boolean
"Computes a route to goal and run the Agent towards it"
"Agent is moved to origin (if any) with :MoveTo() first if it's a humanoid"
"Returns true on computation success, but does not guarantee movement success"
FinishMovement(): void
"Assume completion of current waypoint movement"
"MUST be called for nonhumanoid movement. Does nothing for humanoids"
Pause()
"Temporarily pause movement of Agent."
Resume()
"Resume Agent's movement after :Pause() calls"
Halt()
"Clears information of current route and stop Agent's movement"
GetDistanceSum(startWpIndex: number?, endWpIndex: number?): number
"Returns the total distance needed for movement between any two waypoints in the current route"
"startWpIndex defaults to Path.WaypointIndex"
"endWpIndex defaults to index of goal waypoint"
ChangeConfig(newVal: any): boolean
"Allows safe post-initializaing modification of a Path config (must not be read-only) "
"You must call this when modifying a write-safe public member"
ChangeAgentParams(newParams: AgentParams): boolean
"Change AgentParams during runtime"
"Returns whether the modification was successful"
Destroy(): void
"Schedules destruction of Path on the next heartbeat"
Events
Error: Signal<ErrorType>
"Fires with the ErrorType when an error occured"
Blocked: Signal<blockedWp: PathWaypoint, blockedWpIndex: number>
"Fires when route is blocked, and only when the blockage is ahead of current route"
Paused: Signal
"Fires on successful :Pause() calls"
Resumed: Signal
"Fires on successful :Resume() calls"
Halted: Signal
"Fires on successful :Halt() calls"
AgentParamsChanged: Signal<AgentParams>
"Fires when AgentParams are successfully changed with :ChangeAgentParams()"
WaypointReached: Signal<thisWp: PathWaypoint, thisWpIndex: number>
"Fires when Agent reached a waypoint"
"This fires along with Path.GoalReached when goal is reached"
GoalReached: Signal<goalWp: PathWaypoint>
"Fires when Agent reached the goal"
StartRun: Signal<goal: Vector3, isDirectRun: boolean>
"Fires with the Goal on successful :Run() calls"
"isDirectRun will be true if the run is a direct run allowed by raycast checks success"
DirectRun: Signal<goal: Vector3>
"Fires with the Goal when Agent moves with direct :MoveTo() calls as allowed by raycast checks"
"This fires along with Path.StartRun"
OnJump: Signal
"Fires when the Agent makes a jump to reach the next waypoint"
Destroying: Signal
"Fires a heartbeat prior to the Path's destruction"
Before you go..
- Yes!
- Yes, if I ever needed to manage pathfinding
- No, the module isn’t that good
- This one is better
- Quite the same
- Alternatives are better feature-wise
Credits
- @AlexanderLindholt for Signal module
- @grewsxb4 and @GrayzcaIe for their pathfinding modules from which I take reference
Happy coding!


