Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Could someone explain some of this Pathfinding code?

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!

local p=game:GetService("PathfindingService")
local nodes={}
for _,v in pairs(workspace:GetChildren())do
	if v.Name=="Node"then
		nodes[#nodes+1]=v
	end
end

for _,v in pairs(workspace.NPC:GetChildren())do
	wait(.1)
	Spawn(function()
		while wait()do
			local target=nodes[math.random(1,#nodes)]
			local path=p:ComputeRawPathAsync(v.Torso.Position,target.Position,500)
			if path.Status==Enum.PathStatus.Success then
				local points=path:GetPointCoordinates()
				for _,point in pairs(points)do
					local part,P=workspace:FindPartOnRay(Ray.new(point,Vector3.new(0,-10,0)))
					if part then
						point=P
						points[_]=P
					end
				end
				local br=false
				local distance;
				for _=1,#points,2 do
					local point=points[_]
					if br then
						break
					end
					local count=0
					repeat
						count=count+1
						if count>100 then
							br=true
							break
						end
						v.Humanoid:MoveTo(points[math.min(#points,_+1)])
						if point.Y>=v.Humanoid.Torso.Position.Y-1 then
							v.Humanoid.Jump=true
						end
						distance=(point*Vector3.new(1,0,1)-v.Torso.Position*Vector3.new(1,0,1)).magnitude
						wait()
					until distance<5
				end
			else
				v.Humanoid:MoveTo(v.Torso.CFrame*Vector3.new(0,0,5))
			end
		end
	end)
end

Thanks for reading!

1 answer

✓ Accepted answer

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!

local Pathfinding = game:GetService("PathfindingService")

local nodes = {
	game.Workspace.StartNode,
	game.Workspace.FinishNode,
}

local 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.

if path.Status == Enum.PathStatus.Success then

else
	warn("Path creation unsuccessful")
end

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:

local humanoid = game.Workspace.NPC.Humanoid
local points = path:GetPointCoordinates()

for _, point in ipairs (points) do
	humanoid:MoveTo(point)
	repeat
		local distance = (point - humanoid.Torso.Position).magnitude
		wait()
	until distance <= 3
end

Full Script (only runs once):

local Pathfinding = game:GetService("PathfindingService")

local nodes = {
	game.Workspace.StartNode,
	game.Workspace.FinishNode,
}

local path = Pathfinding:ComputeSmoothAsync(nodes[1], nodes[2], 500)

if path.Status == Enum.PathStatus.Success then
	local humanoid = game.Workspace.NPC.Humanoid
	local points = path:GetPointCoordinates()
	
	for _, point in ipairs (points) do
		humanoid:MoveTo(point)
		repeat
			local distance = (point - humanoid.Torso.Position).magnitude
			wait()
		until distance <= 3
	end
else
	warn("Path creation unsuccessful")
end

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".

local Pathfinding = game:GetService("PathfindingService")

local nodes = game.Workspace.Nodes:GetChildren()

for _, NPC in pairs (game.Workspace.NPCs:GetChildren()) do
	local error = coroutine.wrap(function()
		
		-- All the code will be in here

	end)
	if error then
		error(error)
	end
end

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.

local function SetPointsOnGround(path)
	local points = path:GetPointCoordinates()
	
	for key, point in pairs (points) do
		local part, position = game.Workspace:FindPartOnRay(Ray.new(point, Vector3.new(0, -10, 0)))
		
		if part then
			points[key] = position
		end
	end
	
	return points
end

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.

local function MoveCharacter(points, humanoid)
	local shouldBreak = false
	for _, point in ipairs (points) do
		if shouldBreak then
			break
		end
		
		humanoid:MoveTo(point)

		local count = 0
		repeat
			local distance = (point - humanoid.Torso.Position).magnitude
			
			if point.Y > humanoid.Torso.Position.Y - 1 then
				humanoid.Jump = true
			end
			
			if count >= 50 then
				shouldBreak = true
				break
			end
			
			count = count + 1
			wait()
		until distance <= 4
	end
end

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.

while true do
	local target = nodes[math.random(1, #nodes)]
	local path = Pathfinding:ComputeSmoothPathAsync(NPC.Torso.Position, target.Position, 500)
	
	if path.Status == Enum.PathStatus.Success then
		local points = SetPointsOnGround(path)
		MoveCharacter(points, NPC.Humanoid)
	else
		NPC.Humanoid:MoveTo(NPC.Humanoid.CFrame*Vector3.new(0, 0, 5))
	end
	
	wait()
end

Full Script:

local Pathfinding = game:GetService("PathfindingService")

local nodes = game.Workspace.Nodes:GetChildren()

for _, NPC in pairs (game.Workspace.NPCs:GetChildren()) do
	local error = coroutine.wrap(function()
		local function SetPointsOnGround(path)
			local points = path:GetPointCoordinates()
			
			for key, point in pairs (points) do
				local part, position = game.Workspace:FindPartOnRay(Ray.new(point, Vector3.new(0, -10, 0)))
				
				if part then
					points[key] = position
				end
			end
			
			return points
		end
		
		local function MoveCharacter(points, humanoid)
			local shouldBreak = false
			for _, point in ipairs (points) do
				if shouldBreak then
					break
				end
				
				humanoid:MoveTo(point)

				local count = 0
				repeat
					local distance = (point - humanoid.Torso.Position).magnitude
					
					if point.Y > humanoid.Torso.Position.Y - 1 then
						humanoid.Jump = true
					end
					
					if count >= 50 then
						shouldBreak = true
						break
					end
					
					count = count + 1
					wait()
				until distance <= 4
			end
		end
		
		while true do
			local target = nodes[math.random(1, #nodes)]
			local path = Pathfinding:ComputeSmoothPathAsync(NPC.Torso.Position, target.Position, 500)
			
			if path.Status == Enum.PathStatus.Success then
				local points = SetPointsOnGround(path)
				MoveCharacter(points, NPC.Humanoid)
			else
				NPC.Humanoid:MoveTo(NPC.Humanoid.CFrame*Vector3.new(0, 0, 5))
			end
			
			wait()
		end
	end)
	if error then
		error(error)
	end
end

1 comment

0

Woah! Thank you so much! I will keep reading this! :D

This discussion is preserved as it was. New answers, comments, and votes are closed.