I keep getting this bug where the mob will just move back to its original position, here is an example video:
And here is my code in question:
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local MobPath = PathfindingService:CreatePath()
local NextWaypointIndex = 0
local CurrentPlayer
local ReachedConnection
local BlockedConnection
local ATTACKING_RANGE = 40
local function FindNearestPlayer(Character)
local HumanoidRootPart = Character.HumanoidRootPart
local Distances = {}
local PlayerMapSnapshot = {}
for _, Player in pairs(Players:GetPlayers()) do
local PlayerCharacter = Player.Character or Player.CharacterAdded:Wait()
local PlayerHumanoidRootPart = PlayerCharacter.HumanoidRootPart
PlayerMapSnapshot[Player] = (PlayerHumanoidRootPart.CFrame.Position - HumanoidRootPart.CFrame.Position).Magnitude
table.insert(Distances, PlayerMapSnapshot[Player])
end
for Player, Distance in pairs(PlayerMapSnapshot) do
if Distance == math.min(unpack(Distances)) and Distance <= ATTACKING_RANGE then
CurrentPlayer = Player
return Player
end
end
print("No distance and player were found.")
return nil
end
local function TracePath(Character)
local Humanoid = Character.Humanoid
local HumanoidRootPart = Character.HumanoidRootPart
local NearestPlayer = FindNearestPlayer(Character)
if NearestPlayer == nil then
if CurrentPlayer ~= nil then
NearestPlayer = CurrentPlayer
end
end
-- Precautionary check
if NearestPlayer then
local NearestCharacter = NearestPlayer.Character
local NearestHumanoidRootPart = NearestCharacter.HumanoidRootPart
local success, errormessage = pcall(function()
MobPath:ComputeAsync(HumanoidRootPart.CFrame.Position, NearestHumanoidRootPart.CFrame.Position)
end)
if success and MobPath.Status == Enum.PathStatus.Success then
local Waypoints = MobPath:GetWaypoints()
BlockedConnection = MobPath.Blocked:Connect(function(BlockedWaypointIndex)
if BlockedWaypointIndex > NextWaypointIndex then
BlockedConnection:Disconnect()
TracePath(Character)
end
end)
if not ReachedConnection then
ReachedConnection = Humanoid.MoveToFinished:Connect(function(Reached)
if Reached and NextWaypointIndex < #Waypoints then
NextWaypointIndex += 1
Humanoid:MoveTo(Waypoints[NextWaypointIndex].Position)
else
ReachedConnection:Disconnect()
BlockedConnection:Disconnect()
end
end)
end
NextWaypointIndex = 2
Humanoid:MoveTo(Waypoints[NextWaypointIndex].Position)
end
end
end
local PathTracing = {}
function PathTracing.Init(Character)
while true do
task.wait(1)
TracePath(Character)
end
end
return PathTracing
And this module script has pieces from the Roblox Documentation Hub, and this module is cloned, required, and initialized inside the player.
I’m not sure what’s causing this though, it might be the ComputeAsync part, but I’ll be open to issues that may be the bug, and if it’s the cause, I’ll be fixing it in no time.
I fixed this bug by rewriting it. Here is the new version:
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local MobPath = PathfindingService:CreatePath()
local NextWaypoint = 0
local ReachedConnection
local TargetMovedConnection
local PreviousPlayer
local ATTACKING_RANGE = 40
local MINIMUM_DISTANCE = 5
--[[
1. Get nearest player or current player
2. Use pathfindingservice to calculate routes
]]
local function Reset()
ReachedConnection:Disconnect()
TargetMovedConnection:Disconnect()
ReachedConnection = nil
TargetMovedConnection = nil
end
local function FindNearestPlayer(Character)
local HumanoidRootPart = Character.HumanoidRootPart
local Distances = {}
local PlayerMapSnapshot = {}
for _, Player in pairs(Players:GetPlayers()) do
local PlayerCharacter = Player.Character or Player.CharacterAdded:Wait()
local PlayerHumanoidRootPart = PlayerCharacter.HumanoidRootPart
PlayerMapSnapshot[Player] = (PlayerHumanoidRootPart.CFrame.Position - HumanoidRootPart.CFrame.Position).Magnitude
table.insert(Distances, PlayerMapSnapshot[Player])
end
for Player, Distance in pairs(PlayerMapSnapshot) do
if Distance == math.min(unpack(Distances)) and Distance <= ATTACKING_RANGE then
return Player
end
end
return nil
end
local function FollowPlayer(Character)
local Humanoid = Character.Humanoid
if Humanoid and Humanoid.Health > 0 then
local TargetPlayer = FindNearestPlayer(Character)
if TargetPlayer then
PreviousPlayer = TargetPlayer
local TargetCharacter = TargetPlayer.Character
local TargetHumanoid = TargetCharacter.Humanoid
local success, errormessage = pcall(function()
if TargetHumanoid and TargetHumanoid.Health > 0 then
MobPath:ComputeAsync(Character.PrimaryPart.CFrame.Position, TargetCharacter.PrimaryPart.CFrame.Position)
end
end)
if success and MobPath.Status == Enum.PathStatus.Success then
local Waypoints = MobPath:GetWaypoints()
if not ReachedConnection then
NextWaypoint = 2
if Waypoints[NextWaypoint] then
Humanoid:MoveTo(Waypoints[NextWaypoint].Position)
end
ReachedConnection = Humanoid.MoveToFinished:Connect(function(Arrived)
if Arrived and NextWaypoint < #Waypoints then
NextWaypoint += 1
if Waypoints[NextWaypoint] then
Humanoid:MoveTo(Waypoints[NextWaypoint].Position)
end
elseif Arrived and NextWaypoint >= #Waypoints then
if Waypoints[NextWaypoint] then
Humanoid:MoveTo(Waypoints[NextWaypoint].Position)
end
Reset()
if Humanoid.Health > 0 then
FollowPlayer(Character)
end
end
end)
end
if not TargetMovedConnection then
TargetMovedConnection = TargetHumanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
if TargetHumanoid.MoveDirection.Magnitude > 0 then
Reset()
FollowPlayer(Character)
end
end)
end
end
end
end
end
local PathTracing = {}
function PathTracing.Init(Character)
pcall(function()
repeat
task.wait(1)
FollowPlayer(Character)
until Character:FindFirstChildOfClass("Humanoid").Health <= 0
end)
end
return PathTracing
This is just like the code earlier but the initial movement is run in the if statement of whether or not reached connection exists, thus eliminating the bug of it coming back to its original position.
Now, this works the first time the mob loads but when it dies, a bug happens, and the movement is very choppy (sometimes this doesn’t happen, very selective movement is why this happens). Here is the bug output:
Humanoid is not a valid member of Model "TestDummy" - Studio
22:25:49.887 Stack Begin - Studio
22:25:49.894 Script 'Workspace.Mobs.TestDummy.PathTracing', Line 51 - function FollowPlayer - Studio
22:25:49.894 Script 'Workspace.Mobs.TestDummy.PathTracing', Line 103 - Studio
22:25:49.894 Stack End - Studio
Is there any solution for this as it makes no sense why this happens after all those if statements from the code earlier, also the choppiness. If you’d like a video, I’ll provide, but I think you get what I mean by this.
So I’ve rewritten my code to adjust to my needs, I’d say it’s a massive rewrite as most of my new code is entirely different compared to my code that I’ve posted almost a week ago. Here is my new code:
local Players = game:GetService("Players")
local PathfindingService = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")
-- Configuration
local ATTACKING_RANGE = 40
-- Private functions
local function GetNearestPlayer(Character)
local HumanoidRootPart = Character.HumanoidRootPart
--[[
One is an array, so that
we can use math.min to get the nearest distance
number.
]]
local PlayerMapSnapshot = {}
local Distances = {}
for _, Player in pairs(Players:GetPlayers()) do
local PlayerCharacter = Player.Character or Player.CharacterAdded:Wait()
local PlayerHumanoidRootPart = PlayerCharacter.HumanoidRootPart
PlayerMapSnapshot[Player] = (HumanoidRootPart.CFrame.Position - PlayerHumanoidRootPart.CFrame.Position).Magnitude
table.insert(Distances, PlayerMapSnapshot[Player])
end
for Player, Distance in pairs(PlayerMapSnapshot) do
-- Do not consider dead players
local PlayerCharacter = Player.Character
local PlayerHumanoid = PlayerCharacter.Humanoid
if Distance == math.min(unpack(Distances)) and Distance <= ATTACKING_RANGE and PlayerHumanoid.Health > 0 then
return Player
end
end
return nil
end
local Pathfinding = {}
Pathfinding.__index = Pathfinding
function Pathfinding:Init()
local Humanoid = self.Character.Humanoid
local HumanoidRootPart = self.Character.HumanoidRootPart
while true do
RunService.Heartbeat:Wait()
if Humanoid and Humanoid.Health > 0 then
local NearestPlayer = GetNearestPlayer(self.Character)
if NearestPlayer then
local PlayerCharacter = NearestPlayer.Character
local PlayerHumanoidRootPart = PlayerCharacter.HumanoidRootPart
local MobPath = PathfindingService:CreatePath()
local success, errormessage = pcall(function()
MobPath:ComputeAsync(HumanoidRootPart.CFrame.Position, PlayerHumanoidRootPart.CFrame.Position)
end)
if success and MobPath.Status == Enum.PathStatus.Success then
for _, Waypoint in pairs(MobPath:GetWaypoints()) do
Humanoid:MoveTo(Waypoint.Position)
end
end
end
else
break
end
end
end
function Pathfinding.new(Character)
local self = setmetatable({}, Pathfinding)
self.Character = Character
self:Init()
return self
end
return Pathfinding
I’ve just used OOP as I can separate 2 functionalities (damage and pathfinding) into 2 module scripts as to not clutter my code. But the way the code posted above works is:
Get nearby player
If successful, compute path to follow player
Loop through the paths and follow the player
All of these steps are done in a while true do loop as seen in the statement. This works smoothly and perfectly without bugs. Since it’s a pathfinding mechanic you may as well take the code for your own work.