Hi everyone! I have been making pathfinding npcs and for the past week I have been having major problems with them and cannot find no way to fix my problem. So basically the problem that I have been having with them is that when the round starts and the npcs spawn in they don’t move they just stand there. BUT there is a time, normally every 5-10 times I test it that they actually work and move around. So I have been sitting and scratching my head trying to figure this out. I found out that for the times that they do not work that it does not print past the “path created” print. Yet when it works it prints everything. I have been thinking that there is a possibility that this could be a bug with pathfinding but I am not a high enough rank on the forum to post a bug report. But if there is something wrong with my script please help me and tell me what could be causing it.
Here is the main pathfinding script:
local pathfindingservice = game:GetService("PathfindingService")
local lowertorso = script.Parent:WaitForChild("LowerTorso")
local uppertorso = script.Parent:WaitForChild("UpperTorso")
local runservice = game:GetService("RunService")
local human = script.Parent:WaitForChild("Humanoid")
local rootpart = script.Parent:WaitForChild("HumanoidRootPart")
local attackSound = script.Parent.Head:WaitForChild("Attack1")
local attack = script.Parent:WaitForChild("Animation")
local attackanim = human:LoadAnimation(attack)
attackanim.Priority = Enum.AnimationPriority.Action --action has the highest proiority
local distance = 500
local ignorelist = {}
if script.Parent.HumanoidRootPart:CanSetNetworkOwnership() then
script.Parent.HumanoidRootPart:SetNetworkOwner(nil)
end
function walkRandomly() --this function makes the npc randomly move when no one is around
local xrand = math.random(-100, 100) --this makes it so it picks a random point between -100 studs and 100 studs and makes it move there
local zrand = math.random(-100, 100)
local goal = rootpart.Position + Vector3.new(xrand, 0, zrand) --adds the random locations together so you can use it for pathfinding
local path = pathfindingservice:CreatePath() --pathfinding stuff
path:ComputeAsync(rootpart.Position, goal)
local waypoints = path:GetWaypoints()
if path.Status == Enum.PathStatus.Success then
for i, waypoint in pairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
human:ChangeState(Enum.HumanoidStateType.Jumping)
end
human:MoveTo(waypoint.Position)
local timeout = human.MoveToFinished:Wait(1)
if not timeout then --if the movetofinished did not wait or work then call the following unstuck function
print("Got Stuck")
human:ChangeState(Enum.HumanoidStateType.Jumping)
if human.Health < 1 then
break
end
main()
end
end
else
print("Path Failed")
wait(1)
walkRandomly() --if it failed then recall the walkrandomly
end
end
function findpath(target)--this function makes the path which the npc follows
local path = game:GetService("PathfindingService"):CreatePath() --determing the stuff for the path the npc will follow
path:ComputeAsync(rootpart.Position,target.Position)
local waypoints = path:GetWaypoints()
print("path created")
if path.Status == Enum.PathStatus.Success then
print("Path status good")
for _, waypoint in ipairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
human:ChangeState(Enum.HumanoidStateType.Jumping)
end
human:MoveTo(waypoint.Position)
print("Moving to waypoint")
path.Blocked:Connect(function()
findpath(target) --instead of jumping when the path is blocked it is best to recall the findpath function
end)
local timeout = human.MoveToFinished:Wait(1)
if not timeout then --same as above if it does not do the movetofinished and wait then it redoes the findpath functiona and breaks this function
human:ChangeState(Enum.HumanoidStateType.Jumping)
findpath(target)
break
end
if checkSight(target) then --if the npc sees the target which is defined by a function below
repeat
print("Moving to target")
human:MoveTo(target.Position) --moving the npc to the target
attackTarget(target) --attacks the npc using the attack function
wait(0.1)
if target == nil then
break --all of this checks if the target is true and if not then breaks the repeat loop of the npc moving
elseif target.Parent == nil then
break
end
until checkSight(target) == false or human.Health < 1 or target.Parent.Humanoid.Health < 1 --stops the repeat function if the target dies or if the npc dies
break
end
if (rootpart.Position - waypoints[1].Position).magnitude > 30 then --if the rootpart - the last waypoint distance is greater than 10 (Besically if the target is far away and moves away then it reloads the path)
findpath(target)
break
end
end
end
end
function checkSight(target) --This function uses raycasting to check the distance and hieght of the enemy
local ray = Ray.new(rootpart.Position,(target.Position - rootpart.Position).Unit * 150 )--starts at the root part then shoot to the target from the rootpart and chooses the direction
table.insert(ignorelist, script.Parent)
local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, ignorelist) --ignores all parts on the script.Parent
if hit then --if it hit something
if hit:IsDescendantOf(target.Parent) and math.abs(hit.Position.Y - rootpart.Position.Y) < 10 then --sees if it relates to the target then checks to see if the hieght is similar to the npcs hieght
return true
end
end
return false
end
function findTarget() --this function searches for the target
local distance = 500
local target = nil
local potentialtargets = {}
local seeTargets = {}
for i, v in ipairs(game.Workspace:GetChildren()) do
local humanoid = v:FindFirstChild("Humanoid")
local humanoidRootPart = v:FindFirstChild("HumanoidRootPart")
if humanoid and humanoidRootPart and v.Name ~= script.Parent.Name and v.Name ~= "ArmyZombie" and v.Name ~= "BossZombie" and v.Name ~= "BuffZombie" and v.Name ~= "Demon" and v.Name ~= "DoctorZombie" and v.Name ~= "GoldZombie" and v.Name ~= "PoliceZombie" and v.Name ~= "ReaperZombie" and v.Name ~= "RobberZombie" and v.Name ~= "SpeedZombie" and v.Name ~= "SwiperZombie" and v.Name ~= "Zombie" and humanoid.Health > 0 then
if (rootpart.Position - humanoidRootPart.Position).magnitude < distance then
table.insert(potentialtargets, humanoidRootPart) --adds the target that we found into the potential targets table so we can make sure its within range and is a good target
end
end
end
if #potentialtargets > 0 then --if the number of potential targets is greater than 0
for i, v in ipairs(potentialtargets) do --loops through potential targets
if checkSight(v) then -- runs the checksight function and sends the potential target to check
table.insert(seeTargets, v) --inserts the target into the seetargets
elseif #seeTargets == 0 and (rootpart.Position - v.Position).magnitude < distance then --if the number if targets seen is equal to 0 and if its not within distance then
target = v
distance = (rootpart.Position - v.Position).magnitude
end
end
end
if #seeTargets > 0 then --if number of targets seen is greater than 0 then
distance = 500
for i, v in ipairs(seeTargets) do
if (rootpart.Position - v.Position).magnitude < distance then
target = v
distance = (rootpart.Position - v.Position).magnitude
end
end
end
if target then
if math.random(20) == 1 then
script.Parent.Head.Sound1:Play()
end
end
return target
end
function attackTarget(target) --this function plays the attack function as well as damages
if (rootpart.Position - target.Position).magnitude < 2 then
attackanim:Play()
attackSound:Play()
if target.Parent ~= nil then
target.Parent.Humanoid:TakeDamage(20)
end
wait(0.4)
end
end
local debounce = false
lowertorso.Touched:Connect(function(obj)--this function is if pathfinding messes up
if debounce == false then
debounce = true
if not obj.Parent:FindFirstChild("Humanoid") then
human:ChangeState(Enum.HumanoidStateType.Jumping)
end
end
wait(1)
debounce = false
end)
human.Died:Connect(function()
script:Destroy()
end)
function main() --this is the function that checks to make sure if its a target and if so then starts the path and if not then makes it walk randomly
local target = findTarget()
if target then
human.WalkSpeed = 22
findpath(target)
else
human.WalkSpeed = 12
walkRandomly()
end
end
while wait(0.01) do --this calls the main function
if human.Health < 1 then
break
end
main()
end
Here is the spawning script if needed (Also for testing purposes I have it spawning them every so many seconds but normally I have values that make them spawn when a round starts):
while true do
wait(4)
local NPC = game.ReplicatedStorage.Zombie:Clone()
local NPC2 = game.ReplicatedStorage.SpeedZombie:Clone()
local NPC3 = game.ReplicatedStorage.GoldZombie:Clone()
local NPC4 = game.ReplicatedStorage.BuffZombie:Clone()
local NPC5 = game.ReplicatedStorage.ArmyZombie:Clone()
local NPC6 = game.ReplicatedStorage.Demon:Clone()
local NPC7 = game.ReplicatedStorage.RobberZombie:Clone()
local NPC8 = game.ReplicatedStorage.DoctorZombie:Clone()
local NPC9 = game.ReplicatedStorage.ReaperZombie:Clone()
local NPC10 = game.ReplicatedStorage.PoliceZombie:Clone()
local NPC11 = game.ReplicatedStorage.SwiperZombie:Clone()
local newZombie = math.random(1, 11)
if newZombie == 1 then
NPC.Parent = game.Workspace.Map
NPC.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 2 then
NPC2.Parent = game.Workspace.Map
NPC2.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 3 then
NPC3.Parent = game.Workspace.Map
NPC3.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 4 then
NPC4.Parent = game.Workspace.Map
NPC4.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 5 then
NPC5.Parent = game.Workspace.Map
NPC5.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 6 then
NPC6.Parent = game.Workspace.Map
NPC6.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 7 then
NPC7.Parent = game.Workspace.Map
NPC7.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 8 then
NPC8.Parent = game.Workspace.Map
NPC8.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 9 then
NPC9.Parent = game.Workspace.Map
NPC9.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 10 then
NPC10.Parent = game.Workspace.Map
NPC10.UpperTorso.CFrame = script.Parent.CFrame
end
if newZombie == 11 then
NPC11.Parent = game.Workspace.Map
NPC11.UpperTorso.CFrame = script.Parent.CFrame
end
end