So basically, I’ve been working on a NPC that paths between different points then, once he reaches it’s final destination it basically disappears.
The script works pretty much well, but after like 20-30 seconds the NPCs start to move as if they had a lot of lag (typical pathfinding lag)
Here’s the code:
-- // Client AI made by DegVelopment
-- // Variables
local character = script.Parent.Parent.Parent
local humanoid = character.Humanoid
local root_part = character.PrimaryPart
local character_size = character:GetExtentsSize()
local character_radius = character_size.X
local module_folder = script.Parent.Parent.ModuleScripts
local queue_folder = workspace.TargetParts.Queue
local target_folder = workspace.TargetParts.ParkingSpots
local seat_folder = workspace.Seats
local order = script.Parent.Parent.Assets.Order
local queue_number = 0
local queue_part = nil
--// Services
local pathfindingService = game:GetService("PathfindingService")
local collectionService = game:GetService("CollectionService")
--// Tables
local pathArgs = {
AgentRadius = character_radius,
AgentHeight = character_size.Y,
AgentCanJump = true,
Costs = {
AntiCustomer = math.huge
}
}
-- // Modules
local assets = require(module_folder.NPC_Assets)
--// Events
local returnEvent = game.ServerScriptService.QueueHandler.QueueReturn
local queueEvent = game.ServerScriptService.QueueHandler.QueueFire
-- // Network Owner to the Server, avoiding Client lag
for _, base_part in pairs(character:GetChildren()) do
if base_part:IsA("BasePart") then
base_part:SetNetworkOwner(nil)
end
end
-- // Functions
local function computePathAsync(target)
humanoid:Move(root_part.CFrame.LookVector * 100)
local see_target = false
if assets.checkSight(target) then
see_target = true
end
if see_target == false then
local path = pathfindingService:CreatePath(pathArgs)
local success, errorMsg = pcall(function()
path:ComputeAsync(root_part.Position, target.Position)
end)
if success and path.Status == Enum.PathStatus.Success then
local waypoints = path:GetWaypoints()
for _, waypoint in pairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
humanoid.Jump = true
end
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
if assets.checkSight(target) or assets.checkDist(target, waypoints[#waypoints]) > 30 then
break
end
end
else
warn("Path coudln't compute:",errorMsg)
end
else
humanoid:MoveTo(target.Position)
end
end
local function main() -- // The main function will make the Costumer perform the actions a Costumer would perform
---------------------------------------------------------------------------------------------------------------
-- // Look for a spot in the queue
queueEvent:Fire(character, "Enter")
if queue_part then
while wait() do
computePathAsync(queue_part)
if assets.checkDist(root_part, queue_part) < 5 then break end
end
else
warn("Couldn't find the queue part.")
end
---------------------------------------------------------------------------------------------------------------
-- // Wait for the order to arrive, while progressing in the queue
while order.Value ~= true do
if order.Value == true then break end
computePathAsync(queue_part)
wait()
end
---------------------------------------------------------------------------------------------------------------
-- // Look for a seat to enjoy the meal
queueEvent:Fire(character, "Leave")
local seats_to_choose = {}
for _, seat in pairs(seat_folder:GetChildren()) do
if seat:IsA("Seat") and seat.Occupant == nil then
table.insert(seats_to_choose, seat)
end
end
local random_seat = seats_to_choose[math.random(1, #seats_to_choose)]
random_seat.Disabled = false
while humanoid.Sit == false do
if humanoid.Sit == true then break end
computePathAsync(random_seat)
wait()
end
---------------------------------------------------------------------------------------------------------------
-- // Leave the place
wait(3)
if humanoid.Sit == true then
humanoid.Jump = true
humanoid.Sit = false
end
random_seat.Disabled = true
spawn(function() wait(15) character:Destroy() end)
local new_target = target_folder:GetChildren()[math.random(1, #target_folder:GetChildren())]
while wait() do
if humanoid.Sit == true then
humanoid.Jump = true
humanoid.Sit = false
end
computePathAsync(new_target)
end
end
returnEvent.Event:Connect(function()
if collectionService:HasTag(character, 1) then
queue_number = 1
elseif collectionService:HasTag(character, 2) then
queue_number = 2
elseif collectionService:HasTag(character, 3) then
queue_number = 3
elseif collectionService:HasTag(character, 4) then
queue_number = 4
elseif collectionService:HasTag(character, 5) then
queue_number = 5
elseif collectionService:HasTag(character, 6) then
queue_number = 6
end
queue_part = queue_folder:FindFirstChild("Queue"..queue_number)
end)
if not game:IsLoaded() then game.Loaded:Wait() end
main()
It looks like the pathfinding is causing lag for the NPC after a certain amount of time. One possible reason for this could be that the NPC is continuously computing new paths to its destination, even when it is already close to the destination.
One way to address this issue is to add a check to see if the NPC is already close enough to the target before computing a new path. You could modify the computePathAsync function to include a distance check between the NPC and the target, like this:
local function computePathAsync(target)
humanoid:Move(root_part.CFrame.LookVector * 100)
local see_target = false
if assets.checkSight(target) then
see_target = true
end
if see_target == false then
local path = pathfindingService:CreatePath(pathArgs)
local success, errorMsg = pcall(function()
path:ComputeAsync(root_part.Position, target.Position)
end)
if success and path.Status == Enum.PathStatus.Success then
local waypoints = path:GetWaypoints()
for _, waypoint in pairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
humanoid.Jump = true
end
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
if assets.checkSight(target) or assets.checkDist(target, waypoints[#waypoints]) > 30 or assets.checkDist(root_part, target) < 5 then
break
end
end
else
warn("Path coudln't compute:",errorMsg)
end
else
humanoid:MoveTo(target.Position)
end
end
In this modified version of the function, we added a new check that breaks out of the loop if the NPC is already within a certain distance of the target. This should help reduce the amount of unnecessary pathfinding computations and improve performance.
Another optimization you could make is to increase the wait() time between pathfinding computations. Currently, the NPC is continuously pathfinding as fast as possible, which could also contribute to lag. You could try increasing the wait time to something like wait(0.5) or even higher to reduce the frequency of pathfinding computations.
I hope these suggestions help improve the performance of your NPC pathfinding script!
Thank you so much for your feedback! But somehow the path still stutters like hell… It might be fault of some other script but I don’t know how can I look for the script that it’s causing that much trouble.
Ignore the pathfinding being stupid and how the npc can’t get to the tables, I can easily fix that issue. The only thing that bothers me here is the stuttering.
After adding that, you should remove the for loop that sets network ownership for each part. Don’t know if you got that or not, but I’m telling you in case you didn’t.
Okay so somehow, the pathfinding seems to stutter when this part of the code is running
I tried changing the wait() to wait(.5), but nothing really changes
It might not do much, but I would recommend changing every wait in the script to task.wait(), as well as changing any spawn() to task.spawn(). They all do the same thing, but run and perform much better.
Try this first while I look for anything that could cause it to stutter.
I would like to ask for the place file if you are willing to send it. It does not have to be here either. You can send the file via dms if you would like. I see nothing wrong with the script itself, so it will make it much easier to find a solution with the place file.
spawn(function() wait(15) character:Destroy() end)
local new_target = target_folder:GetChildren()[math.random(1, #target_folder:GetChildren())]
while wait() do
if humanoid.Sit == true then
humanoid.Jump = true
humanoid.Sit = false
end
computePathAsync(new_target)
end
You are spawning a co-routine which uses the while wait() do loop, which has no break condition meaning it will run indefinitely. Placew a break condition, ie if NPC is no longer alive, then break.