Watch 13-07-2026 10-51-25 | Streamable – The clip desmonstrates the error
Currently Im using alignPosition and alignOrientation to move the npcs between pathfinding nodes. So they can actively glide through those nodes to reach the target.
local PathfindingService = game:GetService("PathfindingService")
local pathProfile = {
AgentRadius = 2,
AgentHeight = 5,
AgentCanJump = false,
WaypointSpacing = 5
}
local ChaseState = {}
function ChaseState.OnEnter(bot, data)
local hrp = bot.PrimaryPart
data.LastTargetPos = nil
data.Waypoints = nil
data.CurrentNodeIndex = 1
data.LastNodePos = nil
data.LastLookUpdate = 0
if hrp then
if not hrp:FindFirstChild("BotPathModifier") then
local modifier = Instance.new("PathfindingModifier")
modifier.Name = "BotPathModifier"
modifier.PassThrough = true
modifier.Parent = hrp
end
if not hrp:FindFirstChild("MoveAttachment") then
local attachment = Instance.new("Attachment")
attachment.Name = "MoveAttachment"
attachment.Parent = hrp
local alignPos = Instance.new("AlignPosition")
alignPos.Name = "BotAlignPosition"
alignPos.Attachment0 = attachment
alignPos.Mode = Enum.PositionAlignmentMode.OneAttachment
alignPos.Position = hrp.Position
alignPos.ForceLimitMode = Enum.ForceLimitMode.PerAxis
alignPos.MaxAxesForce = Vector3.new(100000, 0, 100000)
alignPos.MaxVelocity = data.Config.Speed or 16
alignPos.Responsiveness = 40
alignPos.RigidityEnabled = false
alignPos.Parent = hrp
local alignOri = Instance.new("AlignOrientation")
alignOri.Name = "BotAlignOrientation"
alignOri.Attachment0 = attachment
alignOri.Mode = Enum.OrientationAlignmentMode.OneAttachment
alignOri.MaxTorque = 100000
alignOri.Responsiveness = 30
alignOri.Parent = hrp
end
end
end
function ChaseState.OnUpdate(bot, data, deltaTime)
local targetHRP = data.Target and data.Target:FindFirstChild("HumanoidRootPart")
local targetHumanoid = data.Target and data.Target:FindFirstChild("Humanoid")
if not targetHRP or not targetHumanoid or targetHumanoid.Health <= 0 then
return "Idle"
end
local hrp = bot.PrimaryPart
local currentPos = hrp.Position
local targetPos = targetHRP.Position
local alignPos = hrp:FindFirstChild("BotAlignPosition")
local alignOri = hrp:FindFirstChild("BotAlignOrientation")
local dx = targetPos.X - currentPos.X
local dz = targetPos.Z - currentPos.Z
local distanceFlat = math.sqrt(dx * dx + dz * dz)
local now = os.clock()
if distanceFlat <= 2 then
if alignPos and alignPos.MaxVelocity ~= 0 then
alignPos.MaxVelocity = 0
end
if alignOri and (now - data.LastLookUpdate) > 0.2 then
data.LastLookUpdate = now
local lookTarget = Vector3.new(targetPos.X, currentPos.Y, targetPos.Z)
if (lookTarget - currentPos).Magnitude > 1 then
alignOri.CFrame = CFrame.lookAt(currentPos, lookTarget)
end
end
return nil
end
local speed = data.Config.Speed or 16
if alignPos and alignPos.MaxVelocity ~= speed then
alignPos.MaxVelocity = speed
end
local targetDx = targetPos.X - (data.LastTargetPos and data.LastTargetPos.X or 0)
local targetDz = targetPos.Z - (data.LastTargetPos and data.LastTargetPos.Z or 0)
local targetMovedDist = math.sqrt(targetDx * targetDx + targetDz * targetDz)
if not data.LastTargetPos or targetMovedDist > 4 then
data.LastTargetPos = targetPos
task.spawn(function()
local path = PathfindingService:CreatePath(pathProfile)
local success, _ = pcall(function()
path:ComputeAsync(currentPos, targetPos)
end)
if success and path.Status == Enum.PathStatus.Success then
data.Waypoints = path:GetWaypoints()
data.CurrentNodeIndex = 2
data.LastNodePos = nil
end
end)
end
if data.Waypoints and data.CurrentNodeIndex <= #data.Waypoints then
local nodePos = data.Waypoints[data.CurrentNodeIndex].Position
local nx = nodePos.X - currentPos.X
local nz = nodePos.Z - currentPos.Z
local nodeDistanceFlat = math.sqrt(nx * nx + nz * nz)
if nodeDistanceFlat < 4 then
data.CurrentNodeIndex += 1
end
if data.Waypoints[data.CurrentNodeIndex] then
local nextNodePos = data.Waypoints[data.CurrentNodeIndex].Position
if alignPos and data.LastNodePos ~= nextNodePos then
data.LastNodePos = nextNodePos
alignPos.Position = nextNodePos
end
if alignOri and (now - data.LastLookUpdate) > 0.2 then
data.LastLookUpdate = now
local lookTarget = Vector3.new(nextNodePos.X, currentPos.Y, nextNodePos.Z)
if (lookTarget - currentPos).Magnitude > 2 then
alignOri.CFrame = CFrame.lookAt(currentPos, lookTarget)
end
end
end
else
if alignPos and data.LastNodePos ~= targetPos then
data.LastNodePos = targetPos
alignPos.Position = targetPos
end
if alignOri and (now - data.LastLookUpdate) > 0.2 then
data.LastLookUpdate = now
local lookTarget = Vector3.new(targetPos.X, currentPos.Y, targetPos.Z)
if (lookTarget - currentPos).Magnitude > 2 then
alignOri.CFrame = CFrame.lookAt(currentPos, lookTarget)
end
end
end
return nil
end
function ChaseState.OnExit(bot, data)
if bot.PrimaryPart then
local alignPos = bot.PrimaryPart:FindFirstChild("BotAlignPosition")
if alignPos then
alignPos.MaxVelocity = 0
end
end
data.Waypoints = nil
data.LastTargetPos = nil
data.LastNodePos = nil
end
return ChaseState
This is the total chunk of code that is used for moving the npc. Finite state machine approach.
I believe there could be some alternatives that can be used to move the server npc(non-humanoid one, only has a root part). But I havent figured out any!
EDIT: Sorry, I should be more specific about the error… In the video, it might be hard to actually see the issue, but the red box that is moving, keeps on stuttering and trembling whenever it is traversing on its path, it could be some collision issue but Im not sure at all..