In the clip shown above, the movement by the npc is clearly inefficient.
Script:
local NPC = script.Parent
while true do wait()
local Check = workspace:GetPartsInPart(NPC.HumanoidRootPart)
for i, v in ipairs(Check) do
if v.Name == "HumanoidRootPart" or v.Parent:FindFirstChild("HumanoidRootPart") then
local Humanoid = v.Parent:FindFirstChild("Humanoid")
if Humanoid then
Humanoid:TakeDamage(33)
end
end
end
for i, v in ipairs(NPC:GetChildren()) do
if v:IsA("BasePart") or v:IsA("MeshPart") then
v:SetNetworkOwner()
v.CanCollide = false
end
end
local Players = game.Players:GetPlayers()
local Target, Distance = nil, math.huge
for i, v in ipairs(Players) do
if v.Character then
local Character = v.Character
local Magnitude = (NPC.HumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude
if Magnitude < Distance then
Distance = Magnitude
Target = Character.HumanoidRootPart
end
end
end
if Target then
local PathFindingService = game:GetService("PathfindingService")
local Path = PathFindingService:CreatePath()
Path:ComputeAsync(NPC.HumanoidRootPart.Position, Target.Position)
local Part
for i, v in ipairs(Path:GetWaypoints()) do
Part = Instance.new("Part")
Part.Position = v.Position
Part.Anchored = true
Part.Shape = Enum.PartType.Ball
Part.CanCollide = false
Part.Parent = workspace
end
for i, v in ipairs(Path:GetWaypoints()) do
NPC.Humanoid:MoveTo(v.Position)
NPC.Humanoid.MoveToFinished:Wait()
Part:Destroy()
end
else
warn("Target is nil")
end
end
I’m completely out of ideas and can’t think of anything else. I’ve tried searching for similar problems on the devforum but I can’t find any.
I’m pretty sure I can just make light adjustments to my movement and the NPC wouldn’t be able to catch up to me, but I think I could just do Humanoid:MoveTo(Target.Position) if the Target is only a couple of studs away or under a certain magnitude. But the main problem is much more complicated than that.
I can tell you exactly why It is behaving this way. You are setting new target positions only after the NPC has moved to its current target. This results in the choppy pathfinding you are seeing. Additionally, the target that is eventually selected is quickly outdated because you do not account for the movement of the player. Both of these factors combined is your result, I have done similar stuff in the past
a quick suggestion is to comment out this line:
NPC.Humanoid.MoveToFinished:Wait()
and then add a predefined wait at the top like this:
Simple idea, if the player is in the NPC’s line of sight then don’t use pathfinding service.
Run a raycast check at 1st or 5th pathfinding point to see if the player is in the line of sight. If the player is then just directly move to the player.
This is a good suggestion. It would also be easier to implement a system that accounts for player movement this way. You could just raycast to the player every time the NPC tries to pick a target position, and if they are visible then cancel targets and move to intercept
This solved my problem but now I’m facing another problem which is if the player resets their character before the NPC reaches the destination of the v, the npc will still go there.
Thanks for the solution though
Following what @azavier123 said, here’s what is missing:
Delete or comment all this part
local Part
for i, v in ipairs(Path:GetWaypoints()) do
Part = Instance.new("Part")
Part.Position = v.Position
Part.Anchored = true
Part.Shape = Enum.PartType.Ball
Part.CanCollide = false
Part.Parent = workspace
end
for i, v in ipairs(Path:GetWaypoints()) do
NPC.Humanoid:MoveTo(v.Position)
NPC.Humanoid.MoveToFinished:Wait()
Part:Destroy()
end
then replace it by this
local Part = nil --outside of the loop
local function UpdatePart(Position) --outside of the loop
if Part then
Part:Destroy()
end
Part = Instance.new("Part")
Part.Position = Position
Part.Anchored = true
Part.Shape = Enum.PartType.Ball
Part.CanCollide = false
Part.Parent = workspace
end)
-----------------------------------------
if Target then
local PathFindingService = game:GetService("PathfindingService")
local Path = PathFindingService:CreatePath()
Path:ComputeAsync(NPC.HumanoidRootPart.Position, Target.Position)
UpdatePart(Path:GetWaypoints()[2])
NPC.Humanoid:MoveTo(Path:GetWaypoints()[2])
else
warn("Target is nil")
end
The objective here is that the NPC have to move only by a single Waypoint on each 0.1s loop instead of the whole path. This way the path is actively updated to constantly move to the player character. You need to increase weapoints distance a little bit for it to be more effective though.
Also, this line should be at the top of the script, outside of the loop, because it is slowing down the script/code running speed over time.
local PathFindingService = game:GetService("PathfindingService")