Welcome to my first ever tutorial on the devforum, I wanted to make a pathfinding tutorial for such a long time. This may not be the best pathfinding AI tutorial out there. I’m just showing you what i have learned in the past weeks for making a pathfinding AI, without any further ado, let’s begin the tutorial.
note: i suck at explaining scripts :/, also my English kinda sucks as well.
Keep in mind the code under me should be placed in a normal script inside an NPC.
Alright, Lets setup some quick variables
local npc = script.Parent -- the path to the NPC
local human = npc.Humanoid -- getting the humanoid of the npc
local PFS = game:GetService("PathfindingService")
local RUNSERVICE = game:GetService("RunService")
npc.PrimaryPart:SetNetworkOwner(nil)
Well, that was easy, wasn’t it. Lets begin the fun >:), (aka finding the closest target)
–
Let’s create a function with couple of variables in it.
local function findTarget()
local players = game:GetService("Players"):GetPlayers()
local nearesttarget
local maxDistance = 5000 -- distance
end
Alright. Lets loop through the players and check if theres a character!
local function findTarget()
local players = game:GetService("Players"):GetPlayers()
local nearesttarget
local maxDistance = 5000 -- distance
for i,player in pairs(players) do
if player.Character then
--codeeeeeeeeeee
end
end
-- return will go here later in the tutorial
end
EPIC
Okay. let’s do the rest!
local function findTarget()
local players = game:GetService("Players"):GetPlayers()
local nearesttarget
local maxDistance = 5000 -- distance
for i,player in pairs(players) do
if player.Character then
local target = player.Character
local distance = (npc.HumanoidRootPart.Position - target:WaitForChild("HumanoidRootPart").Position).Magnitude
if distance < maxDistance then
nearesttarget = target
maxDistance = distance
end
end
end
return nearesttarget
end
Alright, so what we are doing is getting the target and distance between the NPC, and the target(in this case its the character)
local target = player.Character
local distance = (npc.HumanoidRootPart.Position - target:WaitForChild("HumanoidRootPart").Position).Magnitude
Then checking if the distance, it’s less than the max distance. and setting the nearest target to the target, ANDDD setting the maxDistance to the distance! yay
if distance < maxDistance then
nearesttarget = target
maxDistance = distance
end
We almost forgot, lets add one last line of code to the findTarget function
return nearesttarget
All, together the function should look something like this:
local function findTarget()
local players = game:GetService("Players"):GetPlayers()
local nearesttarget
local maxDistance = 5000 -- distance
for i,player in pairs(players) do
if player.Character then
local target = player.Character
local distance = (npc.HumanoidRootPart.Position - target:WaitForChild("HumanoidRootPart").Position).Magnitude
if distance < maxDistance then
nearesttarget = target
maxDistance = distance
end
end
end
return nearesttarget
end
PHEWWW, We are done with that. Let’s begin by calculating the path
Start by creating an another function, inside that function there’s just going to be 3 lines of code
local function getPath(destination)
local path = PFS:CreatePath()
path:ComputeAsync(npc.HumanoidRootPart.Position, destination)
return path
end
So what we are doing is creating a path using PathFindingService, and using the ComputeAsync(), to compute a path from a start position to an end position. And returning the path.
Thats over, Hmm, Alright then.
Lets create a 3rd function(wow, loads of functions today aye?), with 2 variables inside. One to get the path function, and the 2nd to get the target function.
local function pathFindTo(destination)
local path = getPath(destination)
local target = findTarget()
--rest of the code
end
Okay, Now lets check if there’s a target and check if the target is not dead.
if target and target.Humanoid.Health > 0 then
end
Good. Lets start by creating waypoints. So the AI can walk to them.
for i,waypoint in pairs(path:GetWaypoints()) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
human.Jump =true
end
human:MoveTo(waypoint.Position)
human.MoveToFinished:Wait()
end
So currently, we are looping through the waypoints, using the path variable, and using GetWaypoints() to get them. Simple enough right?, and checking if the waypoint action, its a jump action. Then we are going to set the humanoid jump property to true. And the rest is simple.
All together the function should look something like this.
local function pathFindTo(destination)
local path = getPath(destination)
local target = findTarget()
if target and target.Humanoid.Health > 0 then
for i,waypoint in pairs(path:GetWaypoints()) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
human.Jump =true
end
human:MoveTo(waypoint.Position)
human.MoveToFinished:Wait()
end
end
end
We are almost done!!! Now let’s use RunService
RUNSERVICE.Heartbeat:Connect(function()
--code
end)
The Heartbeat event fires every frame , after the physics simulation has completed.
Now lets check if theres a target if so, use the pathFindTo() function, and set the destination as the target.
RUNSERVICE.Heartbeat:Connect(function()
local target = findTarget()
if target then
pathFindTo(target:WaitForChild("HumanoidRootPart").Position + (target:WaitForChild("HumanoidRootPart").Velocity.Unit * 7)) -- play around with the number 7 to your liking.
end
end)
As you can see the script works very well, It still needs some improvements, But you can do that on your own :>
FULL SCRIPT
local npc = script.Parent
local human = npc.Humanoid
local PFS = game:GetService("PathfindingService")
local RUNSERVICE = game:GetService("RunService")
npc.PrimaryPart:SetNetworkOwner(nil)
local function findTarget()
local players = game:GetService("Players"):GetPlayers()
local nearesttarget
local maxDistance = 5000 -- distance
for i,player in pairs(players) do
if player.Character then
local target = player.Character
local distance = (npc.HumanoidRootPart.Position - target:WaitForChild("HumanoidRootPart").Position).Magnitude
if distance < maxDistance then
nearesttarget = target
maxDistance = distance
end
end
end
return nearesttarget
end
local function getPath(destination)
local path = PFS:CreatePath()
path:ComputeAsync(npc.HumanoidRootPart.Position, destination)
return path
end
local function pathFindTo(destination)
local path = getPath(destination)
local target = findTarget()
if target and target.Humanoid.Health > 0 then
for i,waypoint in pairs(path:GetWaypoints()) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
human.Jump =true
end
human:MoveTo(waypoint.Position)
human.MoveToFinished:Wait()
end
end
end
RUNSERVICE.Heartbeat:Connect(function()
local target = findTarget()
if target then
pathFindTo(target:WaitForChild("HumanoidRootPart").Position + (target:WaitForChild("HumanoidRootPart").Velocity.Unit * 7))
end
end)
Q: Why does the AI twitch in the video?
A: Because, We are generating the path infront of the player by 7 studs. As i just said in the script, Play around with the number.
(target:WaitForChild("HumanoidRootPart").Velocity.Unit * 7)
https://developer.roblox.com/en-us/api-reference/class/RunService
https://developer.roblox.com/en-us/api-reference/event/RunService/Heartbeat