Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
1

Could someone explain some of this Pathfinding code?

Asked by
Xl5Xl5 65
9 years ago

Hey there! I was just wondering if someone can explain each part of this script? I am curious how it works.

Would be great if someone can explain it! Please help!

01local p=game:GetService("PathfindingService")
02local nodes={}
03for _,v in pairs(workspace:GetChildren())do
04    if v.Name=="Node"then
05        nodes[#nodes+1]=v
06    end
07end
08 
09for _,v in pairs(workspace.NPC:GetChildren())do
10    wait(.1)
11    Spawn(function()
12        while wait()do
13            local target=nodes[math.random(1,#nodes)]
14            local path=p:ComputeRawPathAsync(v.Torso.Position,target.Position,500)
15            if path.Status==Enum.PathStatus.Success then
View all 51 lines...

Thanks for reading!

1 answer

Log in to vote
6
Answered by
BlackJPI 2658 Snack Break Moderation Voter Community Moderator
9 years ago

We'll start off by developing a simpler bit of code and then work towards this one.

Throughout this answer we will be using the Roblox PathfindingService. The wiki has a great tutorial for it here.

To start, we will create a program that will attempt to generate a path between two parts: a start node and a finish node. These 'nodes' will simply be parts (or and array of positions) that will tell us information on where the path should start at and be attempting to go.

But how to we create a path? There are two functions that will generate an array of positions between two of our nodes. There is ComputeRawPathAsync (a little faster) and ComputeSmoothPathAsync (a little slower). The raw path will be in more of a grid, where as the smooth path will try to curve the turns to make it a little smoother. Try both of them out to see the difference for your self!

1local Pathfinding = game:GetService("PathfindingService")
2 
3local nodes = {
4    game.Workspace.StartNode,
5    game.Workspace.FinishNode,
6}
7 
8local path = Pathfinding:ComputeSmoothAsync(nodes[1], nodes[2], 500)

Now that we've got the path generated, we have to check to see if the path is complete or not. If the path can be completed it will set the property Statusto Enum.PathStatus.Success. So we can do a quick little if statement to check if the path is good to use. Also, you might want to check out the other PathStatus enums as there is couple different statuses that could be useful.

1if path.Status == Enum.PathStatus.Success then
2 
3else
4    warn("Path creation unsuccessful")
5end

Now we can do what ever we want with the path. For example, you could have an NPC follow the path but getting the points from the path using the GetPointCoordinates function:

01local humanoid = game.Workspace.NPC.Humanoid
02local points = path:GetPointCoordinates()
03 
04for _, point in ipairs (points) do
05    humanoid:MoveTo(point)
06    repeat
07        local distance = (point - humanoid.Torso.Position).magnitude
08        wait()
09    until distance <= 3
10end

Full Script (only runs once):

01local Pathfinding = game:GetService("PathfindingService")
02 
03local nodes = {
04    game.Workspace.StartNode,
05    game.Workspace.FinishNode,
06}
07 
08local path = Pathfinding:ComputeSmoothAsync(nodes[1], nodes[2], 500)
09 
10if path.Status == Enum.PathStatus.Success then
11    local humanoid = game.Workspace.NPC.Humanoid
12    local points = path:GetPointCoordinates()
13 
14    for _, point in ipairs (points) do
15        humanoid:MoveTo(point)
View all 23 lines...

What the script you posted does

The script you posted uses all of what I just taught you but instead generates a coroutine for every player which selects a random node in the world and makes the player move towards that node.

There are some other things in here that help to prevent certain glitches from happening, like the lines 17-23 which place the points on the ground so that they aren't floating and potentially causing errors, and line 47 where it moves the character 5 studs backwards incase it gets stuck.

What I am going to do is break up this script into sections and attempt to make it more readable and easier to understand.

Script Setup

This is pretty straight forward. I did however change spawn to coroutine.wrap so that you could catch errors on the thread. I have also made it so that all the NPCs must be stored in an object called "NPCs" in workspace and all the nodes must be stored in an object called "Nodes".

01local Pathfinding = game:GetService("PathfindingService")
02 
03local nodes = game.Workspace.Nodes:GetChildren()
04 
05for _, NPC in pairs (game.Workspace.NPCs:GetChildren()) do
06    local error = coroutine.wrap(function()
07 
08        -- All the code will be in here
09 
10    end)
11    if error then
12        error(error)
13    end
14end

Setting Points on the ground:

This function works by casting a ray down 10 studs from each point in the path. If a part is hit, then it sets the position of the point to the hit position resulting in the new position sitting on the ground. This is useful for preventing the character from getting stuck in certain situations and for detecting when the player needs to jump.

01local function SetPointsOnGround(path)
02    local points = path:GetPointCoordinates()
03 
04    for key, point in pairs (points) do
05        local part, position = game.Workspace:FindPartOnRay(Ray.new(point, Vector3.new(0, -10, 0)))
06 
07        if part then
08            points[key] = position
09        end
10    end
11 
12    return points
13end

Moving Character:

This function simply loops through all the points and moves the player towards them. While the player is moving, a repeat loop checks to see how far away the player is from the point, if the player needs to jump, and if the player is stuck and should break out of the loop.

01local function MoveCharacter(points, humanoid)
02    local shouldBreak = false
03    for _, point in ipairs (points) do
04        if shouldBreak then
05            break
06        end
07 
08        humanoid:MoveTo(point)
09 
10        local count = 0
11        repeat
12            local distance = (point - humanoid.Torso.Position).magnitude
13 
14            if point.Y > humanoid.Torso.Position.Y - 1 then
15                humanoid.Jump = true
View all 27 lines...

While Loop:

This is the main portion. This is what creates the path and calls the function to make the humanoid move. If the player is in a spot where it can not generate a path, it will attempt to move backwards 5 studs.

01while true do
02    local target = nodes[math.random(1, #nodes)]
03    local path = Pathfinding:ComputeSmoothPathAsync(NPC.Torso.Position, target.Position, 500)
04 
05    if path.Status == Enum.PathStatus.Success then
06        local points = SetPointsOnGround(path)
07        MoveCharacter(points, NPC.Humanoid)
08    else
09        NPC.Humanoid:MoveTo(NPC.Humanoid.CFrame*Vector3.new(0, 0, 5))
10    end
11 
12    wait()
13end

Full Script:

01local Pathfinding = game:GetService("PathfindingService")
02 
03local nodes = game.Workspace.Nodes:GetChildren()
04 
05for _, NPC in pairs (game.Workspace.NPCs:GetChildren()) do
06    local error = coroutine.wrap(function()
07        local function SetPointsOnGround(path)
08            local points = path:GetPointCoordinates()
09 
10            for key, point in pairs (points) do
11                local part, position = game.Workspace:FindPartOnRay(Ray.new(point, Vector3.new(0, -10, 0)))
12 
13                if part then
14                    points[key] = position
15                end
View all 66 lines...
0
Woah! Thank you so much! I will keep reading this! :D Xl5Xl5 65 — 9y
Ad

Answer this question