SuperPath - Advanced Pathfinding Module with Waypoint Optimization, Builtin Raycasting, etc

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 :white_check_mark: :white_check_mark: :white_check_mark:
Waypoint Visualization :white_check_mark: :white_check_mark: :white_check_mark: (customizable colors!)
Waypoint Optimization :cross_mark: :white_check_mark: :white_check_mark: (but more powerful!)
Rate Limiting :white_check_mark: :cross_mark: :white_check_mark:
Autocomplete Support :yellow_circle: :white_check_mark: :white_check_mark:
Partial Path Friendliness :yellow_circle: :white_check_mark: :white_check_mark:
Agent Stuttering Protection :cross_mark: :white_check_mark: :white_check_mark:
Built-in Raycast Checks :cross_mark: :cross_mark: :white_check_mark:
Runtime AgentParams Changing   :cross_mark: :cross_mark: :white_check_mark:
QoL :cross_mark: :white_check_mark: :yellow_circle:

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.Instance does not exist; or
  • RaycastResult.Position:FuzzyEq(goal, 0.2) is true
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:
Screenshot 2026-04-25 102700
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..

Will you use SuperPath?
  • Yes!
  • Yes, if I ever needed to manage pathfinding
  • No, the module isn’t that good
0 voters
What do you think about the module, as compared to alternatives
  • This one is better
  • Quite the same
  • Alternatives are better feature-wise
0 voters

Credits

Happy coding!

33 Likes

You might want to fix the creator store link, it directs to literally roblox.com lol

1 Like

oh mb I accidentially typed the wrong link lol. fixed

Will sure give this one a go - if you say it is better than SimplePath - which was good.. but still gave me stuck bugs at times.

Thanks for your share !! :wink:

1 Like

yeah SimplePath can easily stutter npcs without extra protection. I figured out skipping the first waypoint might fix this problem :slightly_smiling_face:

1 Like

Patch 1.0.1

  • Added TimeLimit enum to SuperPath
  • Added __VERSION attribute to SuperPath module

Click here to get to top of post


For some reason the waypoint for jumping is created too early



-- 1. Standard Roblox Pathfinding Agent Parameters
local standardAgentParams = {
	AgentRadius = 3,
	AgentHeight = 5,
	AgentCanJump = true,
	AgentCanClimb = true
}

-- 2. SuperPath Advanced Configuration
local superPathSettings = {
	-- Raycast check: If the goal is in direct line of sight, bypass the pathfinding 
	-- computation entirely and just walk straight there using :MoveTo()
	RaycastPreRun = true,
	RaycastSettings = {
		FilterType = Enum.RaycastFilterType.Exclude,
		FilteredInstances = {Character}, -- Ignore the Character so the raycast doesn't hit it
		MaxYOffset = 1.5, -- If the goal is too high up (e.g., on a ledge), fallback to pathfinding
		Cooldown = 0 
	},

	-- Waypoint Optimizations: Cleans up Roblox's default pathfinding spaghetti
	DeleteUnneededWaypoints = true, -- Removes redundant waypoints that are too close together
	OnStepupNoJump = true,          -- Stops the Humanoid from jumping on minor step-ups
	OnFallNoJump = true,             -- Prevents the Humanoid from jumping before a downward drop
	Visualize = true
}
-- 3. Initialize the SuperPath Agent
local pathAgent = SuperPath.Agent(Character, standardAgentParams, superPathSettings)

-- 4. Define a Goal and Execute
-- For this example, we'll pick a destination 50 studs away on the X and Z axes
local targetPosition = workspace.Target.Position

-- If you want to move to a specific part instead, use:
-- local targetPosition = workspace:WaitForChild("TargetPart").Position

print("SuperPath: Calculating route and moving...")

-- Start the movement cycle
pathAgent:Run(targetPosition)
2 Likes

Ah I see the issue. The waypoint before the jump waypoint is destroyed which isn’t to be expected. Ill look into this and patch it soon hopefully

2 Likes

Ping me once it is fixed, thank you for the quick responses!

Yeah… I have that problem currently - where NPC’s in Red & Blue teams stutter on their way to their Targets.. while shooting at enemy NPC/Players on the way.

It sure is annoying. Will give it a try a bit later this week. Hope this one solves it.

I have been monitoring the post lol

the stuttering issue persist even when using SuperPath?

It will take me a while to test it - as I have several scripts that the NPC has to go through.
My issue is that being a noob scripter - need to make sure it doesn’t break the game.
But I will reply once I get there :wink:

ATM - I am still going through your modules.

EDIT: I put your Modules in - adjusted the require Paths to them.
But for some reason the NPC does not move now. (working on it).

EDIT #2:
Yeah - It is still Jerky as the NPC moves past an enemy - facing them - and shooting.
Might need to rewrite my NPC script to work with this SuperPath.

It didn’t work initially as SimplePath used “New” instead of SuperPaths “Agent”.
image

And my 2nd issue was :
image

Script adjusted with Assitant:

A work in progress - and am sure it will be better (more tests needed) :smile:

1 Like

Update 1.1.0

  • Exposed Path.MinimumSpacing that replaces the internal 1.5 studs threshold, such that a thisWp where nextWp.Position - thisWp.Position > MinimumSpacing will be removed if DeleteUnneededWaypoints is true
  • Fixed de-jumped waypoints not properly stored in Path.PathWaypoints
  • Fixed waypoint optimization algorithm

Click here to get to top of post
@niltrace

Really cool module you got here! Does it support pathfinding links out-of-the-box? If so then how can I use them in my code?

1 Like

thanks lol. Yeah, it handles link-based movement internally so you just set up pathfinding links (maybe set custom costs if you wish) and it will run the Agent. But the Agent stops moving when, say, teleported to the other side of a portal so you might want to call :Run() repetitively. Might tackle this later

If you want to listen to navigation to custom links, you might do smth like this:

Path.WaypointReached:Connect(function(waypoint)
    if waypoint.Label == "YourLabelHere" then
        -- do something
    end
end)
1 Like

can u give a example script of how u can use pathfinding on a singular part which doesnt have a humanoid?

Wait I found some issues when testing nonhumanoids, yeah sorry about that Ill release a fix soon

I was trying so hard to make non humanoids work but it never worked i hope u can find a fix soon :sob:

1 Like

How about an editable testing place?

1 Like