So I have a Roblox pathfinding script that I use for my horror monster npc but the problem is the pathfinding ai works well in an open environment map, however when it comes to my game map which is basically like a tunnel maze my pathfinding script breaks and the entity doesn’t follow the player that well. I don’t know that much about pathfinding but after reading some posts I think it has to do with the fact I’m using the humanoid:MoveTo() function, which is great in open environments. On top of the script breaking I’m not sure if my pathfinding AI is hogging too many resources, I tried to use events and Runservice.Heartbeat to avoid looping and it works well but it’s hard to set conditions to break the loop. I’m pretty sure I set my agent parameters correctly.
Solutions I’ve tried:
Tweening movement, Raycasting and Messing with debounce intervals.
local Pathfind = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Humanoid = script.Parent:WaitForChild("Humanoid")
local hrt = script.Parent:WaitForChild("HumanoidRootPart")
local animController = Humanoid:FindFirstChild("Animator")
local Folder = Instance.new("Folder")
Folder.Name = "Waypoints"
Folder.Parent = workspace
local targetPlayer = nil
local debounceTime = 0.02
local lastUpdateTime = 0.85
local pathUpdateEvent = Instance.new("BindableEvent")
local Target = workspace.PatrolPoints:GetChildren()
local isMoving = false
local currentDestination = nil
local Walk = script:FindFirstChild("Walk")
local Walktrack = Humanoid:LoadAnimation(Walk)
local Run = script:FindFirstChild("Run")
local RunTrack = Humanoid:LoadAnimation(Run)
local function PlayAnimation(animationId)
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://" .. tostring(animationId)
local track = Humanoid:LoadAnimation(animation)
track:Play()
track:AdjustSpeed(1)
track:Stop()
return track
end
local function ShuffleTarget()
if not isMoving then
if #Target > 0 then
local randompoint = math.random(1, #Target)
local chosenpoint = Target[randompoint]
currentDestination = chosenpoint.Position
isMoving = true
Humanoid.WalkSpeed = 20
if animController then
local currentanim = animController:GetPlayingAnimationTracks()
if currentanim and currentanim.Animation == script.Run then
RunTrack:Stop()
else
Walktrack:Play()
end
end
local path2 = Pathfind:CreatePath({
AgentRadius = Humanoid.RootPart.Size.X / 2,
AgentHeight = Humanoid.HipHeight,
AgentMaxSlope = 45,
Costs = {
Rock = 20
}
})
path2:ComputeAsync(Humanoid.RootPart.Position, currentDestination)
if path2.Status == Enum.PathStatus.Success then
local waypoints = path2:GetWaypoints()
for i, v in ipairs(waypoints) do
local Point = Instance.new("Part")
Point.Anchored = true
Point.Shape = Enum.PartType.Ball
Point.Size = Vector3.new(0.5, 0.5, 0.5)
Point.Position = v.Position + Vector3.new(0, 2, 0)
Point.CanCollide = false
Point.BrickColor = BrickColor.new("Bright green")
Point.Material = Enum.Material.Neon
Point.Transparency = 0
Point.Parent = Folder
Point.Name = "Point" .. tostring(i)
end
for _, waypoint in ipairs(waypoints) do
Humanoid:MoveTo(waypoint.Position)
Humanoid.MoveToFinished:Wait()
end
end
isMoving = false
else
print("No patrol points found.")
end
end
end
local function findNearestPlayer()
local nearestPlayer = nil
local nearestDistance = 200
local players = Players:GetPlayers()
for _, player in ipairs(players) do
local character = player.Character
if character and character ~= script.Parent and character:FindFirstChild("HumanoidRootPart") then
local distance = (hrt.Position - character.HumanoidRootPart.Position).magnitude
if distance < nearestDistance then
nearestPlayer = character
nearestDistance = distance
end
end
end
return nearestPlayer
end
local blockedconnection
local function updatePathToPlayer()
targetPlayer = findNearestPlayer()
if targetPlayer then
local targetPosition = targetPlayer.HumanoidRootPart.Position
if lastPlayerPos ~= targetPosition then
lastPlayerPos = targetPosition
local distanceToPlayer = (hrt.Position - targetPosition).magnitude
if distanceToPlayer <= 200 then
local Path = Pathfind:CreatePath({
AgentRadius = Humanoid.RootPart.Size.X / 2,
AgentHeight = Humanoid.HipHeight,
AgentMaxSlope = 45,
WaypointSpacing = 25
})
Path:ComputeAsync(hrt.Position, targetPosition)
local Waypoints = Path:GetWaypoints()
for _, point in ipairs(workspace.Waypoints:GetChildren()) do
point:Destroy()
end
for i, waypoint in ipairs(Waypoints) do
local Point = Instance.new("Part")
Point.Anchored = true
Point.Shape = Enum.PartType.Ball
Point.Size = Vector3.new(0.5, 0.5, 0.5)
Point.Position = waypoint.Position + Vector3.new(0, 2, 0)
Point.CanCollide = false
Point.BrickColor = BrickColor.new("Bright purple")
Point.Material = Enum.Material.Neon
Point.Transparency = 0
Point.Parent = workspace.Waypoints
Point.Name = "Point" .. tostring(i)
end
Humanoid.WalkSpeed = 40
pathUpdateEvent:Fire(Waypoints)
else
for _, point in ipairs(workspace.Waypoints:GetChildren()) do
point:Destroy()
end
end
end
else
lastPlayerPos = nil
for _, point in ipairs(workspace.Waypoints:GetChildren()) do
point:Destroy()
end
end
end
local function MoveToWaypoint(waypoint)
Humanoid:MoveTo(waypoint.Position)
Humanoid.MoveToFinished:Wait()
end
pathUpdateEvent.Event:Connect(function(waypoints)
for i, waypoint in ipairs(waypoints) do
MoveToWaypoint(waypoint)
Humanoid.MoveToFinished:Wait(2)
if lastPlayerPos == nil or targetPlayer.Humanoid.Health == 0 then
break
end
end
end)
local function Run()
if animController then
local currentanim = animController:GetPlayingAnimationTracks()
if currentanim and currentanim.Animation == script.Walk then
Walk:Stop()
else
return
end
end
RunTrack:Play()
end
local function updateLoop()
if lastPlayerPos == nil then
ShuffleTarget()
else
Run()
end
end
RunService.Heartbeat:Connect(function()
local currentTime = os.clock()
if currentTime - lastUpdateTime >= debounceTime then
lastUpdateTime = currentTime
updatePathToPlayer()
end
updateLoop()
end)
Tunnels and Open Env showcases