Hello everyone! This is another DevForum resource post of mine.
I’d like to showcase a module I’ve worked on and iterating on for quite a while now.
Backstory
As I started building more NPC-heavy systems, such as my TDS-style game for my portfolio, I realized that Roblox pathfinding itself was rarely the difficult part.
The hard part was managing everything around the pathfinding.
My earlier NPC systems worked, but over time several problems started appearing:
- Movement logic became increasingly difficult to maintain
- Path recalculation started becoming messy
- Multiple movement loops could overlap accidentally
- Stopping or resuming NPC movement safely was inconsistent
- Cleanup became error-prone
- State transitions became tightly coupled to movement code
- Scaling behavior across multiple NPCs became difficult to reason about
A lot of the older implementations relied heavily on:
- recursive recalculation
- task loops
- shared boolean flags
- procedural movement chains
Which worked initially, but became harder to scale as AI behavior became more complex.
So instead of continuing to patch the old architecture, I decided to completely redesign the system around a few core ideas:
- explicit movement states
- cancellable path sessions
- structured NPC registration
- observer-driven callbacks
- scoped cleanup
- centralized movement orchestration
The result became this module.
Introducing PathfindingModule
PathfindingModule is a structured NPC path orchestration system built around state-driven movement and waypoint management.
Rather than only focusing on generating paths, the module focuses on organizing and managing the entire movement lifecycle of NPCs in a scalable and maintainable way.
Frameworks Used
- Fusion (state management / observers)
- Promise (async movement handling)
Overview
PathfindingModule provides:
- Structured NPC registration
- Waypoint group management
- State-driven movement
- Automatic cleanup handling
- Callback binding for movement states
- Safe movement cancellation
- Path recalculation support
- Resume / break functionality
Core Concept
Instead of manually managing movement loops for every NPC, you:
- Register NPCs
- Register waypoint groups
- Assign waypoint groups to NPCs
- Start pathfinding
Example:
local Pathfinder = PathfindingModule.New()
Pathfinder:LogObject("Enemies", NPCModel, {
AgentRadius = 2,
AgentHeight = 5,
AgentCanJump = true
})
Pathfinder:LogPathfindGroup("PatrolRoute", waypoints, true)
Pathfinder:RegisterWaypointGroupToInstance(
"PatrolRoute",
"Enemies",
NPCModel
)
Pathfinder:StartPathfinding(
NPCModel,
"Enemies",
"PatrolRoute"
)
The module handles movement execution, path state transitions, cancellation, and cleanup internally.
State System
Each NPC internally operates using explicit movement states such as:
"Idle"
"Moving"
"WaypointReached"
"Finished"
"Break"
"Continue"
"Stuck"
This allows movement behavior to become event-driven rather than relying on procedural loops.
Callback Binding
Callbacks can be attached to specific movement states:
Pathfinder:BindCallback("Enemies",
NPCModel,
"Finished",
"OnFinished",
function()
print("NPC finished path.")
end)
This makes it easier to separate movement orchestration from gameplay behavior.
Recalculation Support
Waypoint groups can optionally enable path recalculation.
If an NPC drifts too far from its original route, the module can recompute a new path dynamically using Roblox’s PathfindingService.
Pathfinder:LogPathfindGroup("Route", waypoints, true)
Path Session Safety
One issue I repeatedly encountered in older systems was overlapping movement execution.
To solve this, the module uses internal path session invalidation through path tokens.
This ensures:
- old movement loops cannot continue running after cancellation
- resumed movement remains synchronized
- path interruptions are handled safely
Example Use Cases
This module works especially well for systems involving:
- Patrol NPCs
- Enemy AI
- Chasing systems
- State-driven AI behavior
- Large NPC groups
- Modular AI architectures
Limitations / Future Improvements
Planned improvements include:
- Better stuck recovery handling
- Smarter dynamic obstacle recovery
- Network ownership utilities
- Optional visualization/debugging tools
- Built-in chasing abstractions
- More advanced waypoint behaviors
Final Thoughts
This module was built primarily to solve architectural and scaling issues I repeatedly encountered while building NPC systems.
Over time, the focus shifted away from simply “moving NPCs” and more toward creating a structured and maintainable movement framework.
I also wanted the setup process itself to remain relatively simple while still supporting more advanced organization internally.
One thing worth mentioning is that this module works especially well when combined with systems such as spatial partitioning.
For example, in enemy AI systems:
- A spatial partition module can be used to retrieve nearby targets efficiently
- The NPC can stop or interrupt its current path once a target is detected
- Combat or attack behavior can then execute independently
- After the interaction finishes, the NPC can resume its previous movement flow
Since the module exposes explicit movement states (Break, Continue, Finished, etc.), you can also bind custom behavior directly to those transitions.
For example:
Pathfinder:BindCallback(
"Enemies",
NPC,
"Break",
"AttackTarget",
function()
print("Execute attack behavior")
end)
In a real AI setup, "Break" could represent:
- target acquisition
- entering combat
- temporary interruption
- animation-driven behavior
- scripted actions
while "Continue" resumes the movement lifecycle afterward.
That flexibility was one of the main goals behind the state-driven architecture. Thank you very much for reading, and any feedback would be greatly appreciated.
PathfindingModule.rbxm (83.7 KB)