@XShadowTheGamerX sorry for late reply. The reason why it’s not working is that it is actually working.
Right after the player joins, PathfindingServe does recognize position, calculates the path and in fact moves the player. The problem is that there is no loop. Now, calculating path constantly for every move is obviously not a good idea, because the movement isn’t only stuttery, but script is performance heavy. What can we do? We can calculate the path less frequently. Correct, although NPC won’t follow player real-time, but instead take rapidly corrected paths. It’s going to look like NPC is a little lost. Ideally, we would use :MoveTo() function. It provides smooth movement, paths are direct. Back to the original issue. :MoveTo() is great, but doesn’t avoid obstacles. Ideally, we would somehow detect blockades (a good idea is raycasting), and only then trigger pathfinding.
Originally, I wanted to use
humanoid.MoveDirection:Dot(humanoid.MoveDirection) > 0
-- or (the above line seems faster and less demanding)
humanoid.MoveDirection.Magnitude > 0
to check whether NPC is moving or not, but apparently it’s dot product of it’s movement commonly falls to zero (which means it stops often for very short amounts of time). Calls for pathfinding are then frequent, and movement is stuttery again.
Because I am a bit in a hurry, I quickly created a coroutine, checking humanoid’s movement status every 3 to 4 seconds. This doesn’t seem too performance demanding, however, there certainly is a better solution, which also involves slightly different, smarter, but more complex script.
This is really not my proudest script, but it’s a solid solution.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")
local humanoid = script.Parent
local hrp = script.Parent.Parent:FindFirstChild("HumanoidRootPart")
local find_path = false
local playerRootPart
local function checkForMovement()
while (true) do
if (humanoid.MoveDirection:Dot(humanoid.MoveDirection) == 0) then
wait(.5)
if (humanoid.MoveDirection:Dot(humanoid.MoveDirection) == 0) then
find_path = true
end
end
wait(3)
end
end
local function chasePlayer(player)
if (not playerRootPart) then return; end
coroutine.wrap(checkForMovement)()
while (true) do
if (not find_path) then
humanoid:MoveTo(playerRootPart.Position)
else
local path = PathfindingService:ComputeRawPathAsync(hrp.Position, playerRootPart.Position, 512)
local points = path:GetPointCoordinates()
for p = 3, #points do
humanoid:MoveTo(points[p])
humanoid.MoveToFinished:Wait()
end
find_path = false
end
RunService.Heartbeat:Wait()
end
end
Players.PlayerAdded:Connect(function(player)
if (not player.Character) then player.CharacterAdded:Wait() end
playerRootPart = player.Character:WaitForChild("HumanoidRootPart")
chasePlayer(player)
end)
EDIT (2021-03-06)
@XShadowTheGamerX the script currently belongs inside humanoid of your NPC (like it was origianlly in your script). Should you decide to move it elsewhere, you have to change the paths as well.