My current code uses a vast amount of the CPU and continues to increase over time.
After 15 minutes in-game:
After 25 minutes in-game:
--^ Game Services
local collectionService = game:GetService("CollectionService")
local pathfindingService = game:GetService("PathfindingService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local assetsFolder = replicatedStorage:WaitForChild("Assets[Folder]")
local animationFolder = assetsFolder:WaitForChild("Animations[Folder]")
local workspace = game:GetService("Workspace")
local entityContainer = workspace:WaitForChild("EntityContainer")
local pathfindingContainer = workspace:WaitForChild("PathfindingContainer")
function tagHumanoid(instance:Instance) --^ Tags the given "instance" with the "Entity" tag using the Collection Service
collectionService:AddTag(instance:FindFirstChild("HumanoidRootPart"), "Interactable")
end
function randomPosition() -- ^ Returns a random position within the "pathfindingContainer"
local pathfindingParts = pathfindingContainer:GetChildren()
local randomPartIndex = math.random(#pathfindingParts)
local randomPart = pathfindingParts[randomPartIndex]
local randomPos = (randomPart.CFrame * CFrame.new(
(math.random() - 0.5) * randomPart.Size.X,
(math.random() - 0.5) * randomPart.Size.Y,
(math.random() - 0.5) * randomPart.Size.Z
)).Position
return randomPos
end
local function AiPathfindingController(entity, destination) --^ Pathfinding Function
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local humanoid = entity:FindFirstChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")
local fowardAnim = animationFolder:WaitForChild("PenguinFoward")
local idleAnim = animationFolder:WaitForChild("PenguinIdle")
local forwardAnimTrack = animator:LoadAnimation(fowardAnim)
local idleAnimTrack = animator:LoadAnimation(idleAnim)
forwardAnimTrack.Priority = Enum.AnimationPriority.Movement
idleAnimTrack.Priority = Enum.AnimationPriority.Idle
idleAnimTrack:Play()
local randomPosition = randomPosition() --^ assigns random Vector3 value to variable
local path
local defaultEntityPath = pathfindingService:CreatePath({ --^ Path Parameters Table
["AgentRadius"] = 2.5,
["AgentHeight"] = 3,
["AgentCanJump"] = false,
["AgentJumpHeight"] = 7,
["AgentCanClimb"] = false,
["AgentMaxSlope"] = 45,
["WaypointSpacing"] = 2,
["Cost"] = {
["Water"] = 20,
}
})
local largeEntityPath = pathfindingService:CreatePath({ --^ Path Parameters Table
["AgentRadius"] = 5,
["AgentHeight"] = 7,
["AgentCanJump"] = false,
["AgentJumpHeight"] = 7,
["AgentCanClimb"] = false,
["AgentMaxSlope"] = 45,
["WaypointSpacing"] = 2,
["Cost"] = {
["Water"] = 20,
}
})
if entity.Name == "GiantPenguin" then
path = largeEntityPath
else
path = defaultEntityPath
end
--^ Compute the path
local success, errorMessage = pcall(function()
path:ComputeAsync(entity.PrimaryPart.Position, destination)
end)
if entity.Parent == entityContainer then
if success and path.Status == Enum.PathStatus.Success then
waypoints = path:GetWaypoints() --^ Get the path waypoints
--[[
for i, waypoint in pairs(waypoints) do --^ Visualizes Waypoints
local visualWaypoint = Instance.new("Part")
visualWaypoint.Size = Vector3.new(0.3, 0.3, 0.3)
visualWaypoint.Anchored = true
visualWaypoint.CanCollide = false
visualWaypoint.Material = Enum.Material.Neon
visualWaypoint.Shape = Enum.PartType.Ball
visualWaypoint.Position = waypoint.Position
visualWaypoint.Parent = workspace
end
]]
--^ Detect if path becomes blocked
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
--^ Check if the obstacle is further down the path
if blockedWaypointIndex >= nextWaypointIndex then
--^ Stop detecting path blockage until path is re-computed
blockedConnection:Disconnect()
--^ Call function to re-compute new path
AiPathfindingController(entity, destination)
end
end)
--^ Detect when movement to next waypoint is complete
if not reachedConnection then
reachedConnection = entity:FindFirstChild("Humanoid").MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
--^ Increase waypoint index and move to next waypoint
nextWaypointIndex += 1
entity:FindFirstChild("Humanoid"):MoveTo(waypoints[nextWaypointIndex].Position)
else
forwardAnimTrack:Stop()
idleAnimTrack:Play()
path:Destroy()
reachedConnection:Disconnect()
blockedConnection:Disconnect()
wait(math.random(1, 15))
AiPathfindingController(entity, randomPosition)
end
end)
end
--^ Initially move to second waypoint (first waypoint is path start; skip it)
nextWaypointIndex = 2
entity:FindFirstChild("Humanoid"):MoveTo(waypoints[nextWaypointIndex].Position)
forwardAnimTrack:Play()
else
--print(path.Status)
warn("Path not computed!", errorMessage)
AiPathfindingController(entity, destination)
end
end
end
entityContainer.ChildAdded:Connect(function(entity)
local animator = Instance.new("Animator", entity:FindFirstChild("Humanoid"))
--print(entity)
tagHumanoid(entity) --^ Tags the child that was added to "pathfindingContainer" with the "Entity" tag
AiPathfindingController(entity, randomPosition())
local humanoid = entity:FindFirstChild("Humanoid") --^ Checks instance for a humanoid
if humanoid then
for i, parts in pairs(entity:GetDescendants()) do --^ Loops through all children of the instance
if parts:IsA("BasePart") or parts:IsA("MeshPart") or parts:IsA("UnionOperation") then --^ Checks if the child is a part
parts.CollisionGroup = "Entity" --^ Sets the collision group of the part to "Entity"
end
end
end
end)
I’m sure there are solutions to this; I’ve played several games that handle well over 200-300+ NPCs with less than 5ms CPU usage. I’m hoping someone can explain why my code uses so much of the CPU and why the CPU usage continually increases over time. As well as an overall solution to the problem. Thank you.