How To Use Pathfinding Service In Roblox

Hello everyone! This is my first tutorial. Please correct me anything if I taught something wrong.

In this post, I will be teaching you guys on how to use the Pathfinding service in Roblox! This tutorial is specially for people who are decent at scripting and new to this service. Anyway without ay further ado...

LET’S GET STARTED!

What the heck is Pathfinding service?

In Roblox, PathfindingService is a service used to find a clear path between 2 points/destination. Imagine an NPC that can walk with this service. Pretty uninteresting right? Well, with PathfindingService, the NPC will find a clear path to walk to it’s destination without getting stuck at blocking obstacles. This will make the NPC smart enough to get to a specific position.

Why use PathfindingService?

This is because, as I’ve stated earlier, PathfindingService allows an NPC to find a clear path between 2 destination points. If you get the idea, a clear path means a path with nothing to block the path. You might be asking, why can’t we just use the MoveTo() function from the humanoid instead? This is because MoveTo() function will ONLY find the fastest path, which is a straight line, from one point to an end point. This makes it hard for the NPC to reach because imagine there’s gaps between the path, blocking obstacles, jumping platforms and even more! With PathfindingService, this issue will be resolved.

Any more things we should know about it?

From this link.

How can we use it?

Glad you asked! Follow the steps below.

  1. Make sure you use a server script for this, and maybe try to parent it in a NPC model so we can script more easily.

  2. Remove the code inside it and get the service of it.

local pathfinding = game:GetService("PathfindingService")
  1. Now, let’s create some variables for the NPC’s root part, humanoid and the destination/position we want it to walk to.
local pathfinding = game:GetService("PathfindingService")

local rootPart = script.Parent:WaitForChild("HumanoidRootPart") or script.Parent:WaitForChild("Torso")
local humanoid = script.Parent:WaitForChild("Humanoid")
local destination = Vector3.new(30,0,0) -- I'm gonna use vector3 instead.
  1. Cool! Now we can start the real stuffs here. We need to create a path so we can tell the NPC to walk to that destination.
local pathfinding = game:GetService("PathfindingService")

local rootPart = script.Parent:WaitForChild("HumanoidRootPart") or script.Parent:WaitForChild("Torso")
local humanoid = script.Parent:WaitForChild("Humanoid")
local destination = Vector3.new(30,0,0) -- I'm gonna use vector3 instead.

local path = pathfinding:CreatePath()
  1. Alright, now we need to give the two points to the path variable so that we will successfully create a path between the starting point and the ending point.
local pathfinding = game:GetService("PathfindingService")

local rootPart = script.Parent:WaitForChild("HumanoidRootPart") or script.Parent:WaitForChild("Torso")
local humanoid = script.Parent:WaitForChild("Humanoid")
local destination = Vector3.new(30,0,0) -- I'm gonnause vector3 instead.

local path = pathfinding:CreatePath()
path:ComputeAsync(rootPart.Position,destination)

Okay, you might be a bit confused. But let me explain. We re calling our path variable and then we use a function called ComputeAsync() which will create a clear path between 2 points. The first parameter of the function is the starting point of our path, in this case we use rootPart.Position as the starting point, then we use destination as our ending point of the path, which is the second argument we need to pass in this function.

NOTE: IF YOU'RE NOT USING VECTOR3 AS THE DESTINATION, YOU CAN USE A PART'S POSITION AS THE ENDING POINT. MAKE SURE TO ADD .Position !!! WHICH WILL BE LIKE ComputeAsync(rootPart.Position,part.Position).

  1. Now that we have computed the path, it will provide a series of waypoints, which we will tell the NPC to move to each waypoint until it reached it’s destination.
local pathfinding = game:GetService("PathfindingService")

local rootPart = script.Parent:WaitForChild("HumanoidRootPart") or script.Parent:WaitForChild("Torso")
local humanoid = script.Parent:WaitForChild("Humanoid")
local destination = Vector3.new(30,0,0) -- I'm gonna use vector3 instead.

local path = pathfinding:CreatePath()
path:ComputeAsync(rootPart.Position,destination)

local waypoints = path:GetWaypoints()
  1. Now, we will use a for loop so that we can tell the NPC to move to each waypoint after they’ve reached the previous ones.
local pathfinding = game:GetService("PathfindingService")

local rootPart = script.Parent:WaitForChild("HumanoidRootPart") or script.Parent:WaitForChild("Torso")
local humanoid = script.Parent:WaitForChild("Humanoid")
local destination = Vector3.new(30,0,0) -- I'm gonna use vector3 instead.

local path = pathfinding:CreatePath()
path:ComputeAsync(rootPart.Position,destination)

local waypoints = path:GetWaypoints()

for i,v in pairs(waypoints) do
    hum:MoveTo(v.Position)
    if v.Action == Enum.PathWaypointAction.Jump then
        hum.Jump = true
    end
    hum.MoveToFinished:Wait()
end

Okay, so what we did is, for every waypoint that’s being iterated in the table of waypoints, we tell the NPC to move to that waypoint using MoveTo(). The gaps between each waypoint is small enough for the NPC to walk to the next waypoint in a straight line. Then, you’ll notice there’s an if statement to it. This checks if whether the NPC reaches a waypoint that needs the humanoid to jump or not. Waypoints have a property called Action and this have 2 values, Jump and Walk. The waypoint will determine whether it’s action is either Jump or Walk. So, we use an if statement to check if the action is jump using an enumeration of PathWaypointAction. If it’s true, we will set the humanoid’s jump to true.

And this should work now! Play the game and see the magic happens. Due to my trashy laptop, I’m unable to record it working in Roblox Studio.

And there you go! You successfully make a smart NPC!

Wait! How can we make it so that when it handles blocked paths?

This got me hard when I was trying to find a solution of it. While you can actually use path.Blocked event, I can’t manage to find how to use this event to handle this issue. But, the best thing we can solve this issue is to create a new path and compute it constantly.

So for this, we will use RunService.Heartbeat as an alternative way for while true do loop. Then, instead of making a script that’s not good-looking, we will create functions which will do a separate job. So we will create a function that creates a path and returns it, then another function which will tell the NPC to move to each waypoints. The key is to tell the script to constantly create a new path, and compute it, as creating a path and compute it only once only keeps that path forever, so if we do this method, it will create a new clear path constantly.

So if you want the full code instead of just trying to create the functions by yourself, here it is.

NOTE: I wouldn’t recommend you guys to copy this code. Try to be creative and use your brain instead!

Full code
local pathfinding = game:GetService("PathfindingService")
local run = game:GetService("RunService")

local rootPart = script.Parent:WaitForChild("HumanoidRootPart") or script.Parent:WaitForChild("Torso")
local humanoid = script.Parent:WaitForChild("Humanoid")
local destination = Vector3.new(30,0,0) -- I'm gonna use vector3 instead.

local function GetPath(destination) 

    local path = pathfinding:CreatePath()   

    if typeof(destination) == "Vector3" then -- This line is untested yet. Please tell me if it works.
        path:ComputeAsync(rootPart.Position,destination)
    else
        path:ComputeAsync(rootPart.Position,destination.Position)
    end

    return path
end)

local function WalkToWaypoints(tableWaypoints)
    for i,v in pairs(tableWaypoints) do
        hum:MoveTo(v.Position)
        if v.Action == Enum.PathWaypointAction.Jump then
        hum.Jump = true
        end
        hum.MoveToFinished:Wait()
    end
end

local function WalkTo(destination)
    local path = GetPath(destination)
    if path.Status == Enum.PathStatus.Success then -- the path will return either success as true value or nopath as false value if it's computed.
        WalkToWaypoints(path:GetWaypoints())
    end
end)

run.Heartbeat:Connect(function()
    WalkTo(destination)
end)
Summary

Pathfinding service sure helped NPC to be smart enough to do a lot of stuffs. Let’s hope it will get a better update/upgrade from Roblox in the future.

Thank you for reading my post! Again, please leave feedbacks and any mistakes and corrections under this post. I will update the post if I did anything wrong.

EDIT 1: I made a spelling mistake while spelling humanoid in the scripts. I accidentally spell it hum as humanoid. If you wanna change it, you can use Ctrl + H or Cmd + H while typing in the script to change these spellings immediately. Sorry for the mistake!

Tutotial made by,
@ItzMeZeus_IGotHacked

5 Likes

If anyone would like to read more on this topic, take a peek at this article:

2 Likes

Thank you for your contribution. May I know if there’s anything wrong with my tutorials?

1 Like

Looks great, maybe a little less enlarged text however. On mobile, it becomes tough to read.

3 Likes

Thank you, let’s just hope Roblox would try to scale the UI more perfectly on here. This is actually my first time doing a tutorial on here with all my ideas on making this possible. Once again, thank you for your suggestion! I will try and continue to make more tutorials. :smiley:

Good contribution, I highly appreciate this thread as it explains the concept very well and without too much complexity added to it, although I am not sure about this segment of the code

is it safe though for the performance? Since Pathfinding might use many calculations and execute a lot of code. If not, any other performance-friendly way to do it?

2 Likes

I can guaranteed this is 100% safe, as this won’t crash client’s devices. If you use a while true do loop, it would constantly execute the same code every single millisecond which will eventually crash the game. Heartbeat does the same thing but it depends on the server’s performance. If it’s good, there should be like a few milliseconds gap which wouldn’t crash the clients.

1 Like

It’s actually really useful, thanks!

1 Like