Yet another path finding issue

I am new to scripting LUA and Roblox. I decided to learn basic mechanics and here is my attempt to understand basic pathfinding.

I took basic code from here:

And decided to try and scale it up. I added height so that the zombies would need to jump, a falling terrain object to block paths and multiple destinations.

The script seems to work ok for a short while but quickly you see that zombies stop pathing and have incredible lag. No errors just a pause.

What am I doing wrong?

I have thought of swapping to a simpler design such as the one posted by Little_Joes here: Problem With Pathfinding Service - #3 by HEMAN_9

But If I just copied answers I don’t learn and there is something to be learned through this failure.

Attached is my script.
See:

  • ReplicatedStorage.Zombie.pathfinding
  • ServerScriptService.Spawner

Pathfinding.rbxl (37.7 KB)

ServerScriptService.Spawner
local ZOMBIE_COUNT = 10
local Players = game:GetService(“Players”)
while #Players:GetPlayers() < 1 do
Players.PlayerAdded:Wait()
end

for i=1, ZOMBIE_COUNT, 1 do
	local delay = math.random(1,100)*.03
	print("Spawning zombie"..i.." then waiting "..delay)
	local zombie = game.ReplicatedStorage.Zombie:Clone()
	zombie.Parent = game.Workspace
	zombie.Name = zombie.Name..i
	zombie.Zombie.WalkSpeed = 64
	wait(delay)
end

ReplicatedStorage.Zombie.pathfinding
local SHOW_WAYPOINTS = true

local Players = game:GetService("Players")
while #Players:GetPlayers() < 1 do
	Players.PlayerAdded:Wait()
end
script.Parent:WaitForChild("HumanoidRootPart")
game.Workspace:WaitForChild("PathDestinations")

local zombie = script.Parent
local humanoid = zombie.Zombie
local destinations = game.Workspace.PathDestinations:GetChildren()
local destinationIndex = math.random(1,#destinations)
local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath()
local waypoints
local currentWaypointIndex

-- Just for troubleshooting
local moveToCalls = 0
local onWaypointReachedCalls = 0


local function showWaypointPath(waypoints)
	for i, waypoint in pairs(waypoints) do
		local part = Instance.new("Part")
		part.Shape = "Ball"
		part.Material = "Neon"
		part.Size = Vector3.new(0.6, 0.6, 0.6)
		part.Position = waypoint.Position
		part.Anchored = true
		part.CanCollide = false
		part.Parent = script.parent.Path
		part.Name = "path_"..i
	end
end

local function deleteWaypointPath()
	for _, wp in ipairs(script.parent.Path:GetChildren()) do
		wp:Destroy()
	end
end

local function getNewDestination()
	local potentialDestination
	repeat
		potentialDestination = math.random(1,#destinations)
	until potentialDestination ~= destinationIndex

	return potentialDestination	
end



local function walkToNextWaypoint()
	currentWaypointIndex = currentWaypointIndex + 1
	--print( "Moving "..zombie.Name.." to waypoint "..currentWaypointIndex.."/"..#waypoints.." on way to "..destinations[destinationIndex].Name.." moveToCalls: "..moveToCalls.." onWaypointReachedCalls: "..onWaypointReachedCalls)
	if waypoints[currentWaypointIndex].Action == Enum.PathWaypointAction.Jump then
		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		--humanoid.Jump = true
	end

	moveToCalls = moveToCalls + 1
	humanoid:MoveTo(waypoints[currentWaypointIndex].Position)
end

local function startWalkingToDestination()
	currentWaypointIndex = 0
	walkToNextWaypoint()
end

local function stopWalking()
	if SHOW_WAYPOINTS then
		deleteWaypointPath()
	end
	humanoid:MoveTo(zombie.HumanoidRootPart.Position)
end

local function createNewPath()
	path:ComputeAsync(zombie.HumanoidRootPart.Position, destinations[destinationIndex].Position)
	if path.Status == Enum.PathStatus.Success then
		waypoints = path:GetWaypoints()
		if SHOW_WAYPOINTS then
			showWaypointPath(waypoints)
		end

		startWalkingToDestination()
	else
		print(zombie.Name.." path not found to ".. destinations[destinationIndex].Name.." moveToCalls: "..moveToCalls.." onWaypointReachedCalls: "..onWaypointReachedCalls)
		stopWalking()
	end
end

local function onPathBlocked(blockedWaypointIndex)
	print(zombie.Name.." is BLOCKED at index "..blockedWaypointIndex.." trying to reach destination "..destinations[destinationIndex].Name.." moveToCalls: "..moveToCalls.." onWaypointReachedCalls: "..onWaypointReachedCalls)
	if blockedWaypointIndex > currentWaypointIndex then
		createNewPath()
	end
end

local function onWaypointReached(reached)
	onWaypointReachedCalls = onWaypointReachedCalls + 1
	if reached and waypoints and currentWaypointIndex < #waypoints then
		walkToNextWaypoint()
	elseif waypoints and currentWaypointIndex == #waypoints then	
		if SHOW_WAYPOINTS then
			deleteWaypointPath()
		end
		print(zombie.Name.." reached the destination "..destinations[destinationIndex].Name.." moveToCalls: "..moveToCalls.." onWaypointReachedCalls: "..onWaypointReachedCalls)
		
		destinationIndex = getNewDestination()
		print("New destination for "..zombie.Name.." "..destinations[destinationIndex].Name.." moveToCalls: "..moveToCalls.." onWaypointReachedCalls: "..onWaypointReachedCalls)
		createNewPath()
	else
		print(zombie.Name.." hasn't reached the destination "..destinations[destinationIndex].Name.." moveToCalls: "..moveToCalls.." onWaypointReachedCalls: "..onWaypointReachedCalls)
		createNewPath()
	end
end


humanoid.MoveToFinished:Connect(onWaypointReached)
path.Blocked:Connect(onPathBlocked)

createNewPath()

Attempted:

local SHOW_WAYPOINTS = true
no change

waypoints = nil
no change

removing
path.Blocked:Connect
no change

local function createNewPath()
	local path = PathfindingService:CreatePath()
	path:ComputeAsync(...)

no change

path:Destroy()
no change

local path = PathfindingService:FindPathAsync
no change

Added the following to the Spawner script prior to the loop that does Zombie:Clone:

local PhysicsService = game:GetService("PhysicsService")
PhysicsService:CreateCollisionGroup("NPC")
PhysicsService:CollisionGroupSetCollidable("NPC","NPC",false)
for i, v in pairs(game.ReplicatedStorage.Zombie:GetChildren()) do
   if v:IsA("BasePart") then
      PhysicsService:SetPartCollisionGroup(v, "NPC")
   end
end

no change although the zombies did stop running into each other.

This is showing a LOT of promise as being the fix. It was proposed to address another path finding issue in a different thread. When I first came across it I overlooked it as I didn’t see how it could be relevant. Mostly because I still do not understand what it does and how it could possibly be the root cause of my issue. It has easily run 15x times as long as it normally takes to stall a NPC. I may run it all night to see if it is still healthy in the morning.

	for i, v in pairs(zombie:GetChildren()) do
		if v:IsA("BasePart") then
			v:SetNetworkOwner(nil)
		end
	end

Does anyone know what the above does and why it would address my issue?

Solution was presented in a different path finding thread by @IronRobotStudios here:

And the physics preventing NPCs from colliding and also solution by @IAmPinleon here:

I probably spent 15hours on this stupid bug only to have it fixed in a way had nothing to do with my code! :frowning: