Claude Skill

roblox-npcs

Build or debug Roblox NPC navigation and behavior. Use for PathfindingService, modifiers and links, blocked paths, patrol or chase logic, streaming, and scaling NPC counts.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download nonlooped-roblox-suite-roblox-npcs-2904c16.zip · 14 KB
Part of nonlooped/roblox-suite — 15 skills

Install

skills CLI npx skills add https://github.com/nonlooped/roblox-suite/tree/main/roblox-npcs
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install nonlooped-roblox-suite@llmmart
Git git clone https://github.com/nonlooped/roblox-suite.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole nonlooped/roblox-suite collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

roblox-npcs

Official sources:

Use PathfindingService for navigation on Roblox's navigation mesh. Build patrol, chase, and other behavior around its computed paths.

PathfindingService basics

Create a path:

local PathfindingService = game:GetService("PathfindingService")

local path = PathfindingService:CreatePath({
    AgentRadius = 2,
    AgentHeight = 5,
    AgentCanJump = true,
    AgentCanClimb = false,
    WaypointSpacing = 4,
    Costs = {
        Water = 20,
        DangerZone = math.huge,
    }
})
path.CalculationSecondsTimeout = 1

Compute and follow:

local humanoid = character:WaitForChild("Humanoid")
local rootPart = character:WaitForChild("HumanoidRootPart")

local success, err = pcall(function()
    path:ComputeAsync(rootPart.Position, endPos)
end)

if success and path.Status == Enum.PathStatus.Success then
    local waypoints = path:GetWaypoints()
    -- follow waypoints with Humanoid:Move()
end

Agent parameters

Parameter Default Purpose
AgentRadius 2 studs Minimum clearance from obstacles
AgentHeight 5 studs Vertical clearance
AgentCanJump true Allows jump waypoints
AgentCanClimb false Allows climbing truss parts
WaypointSpacing 4 studs Distance between intermediate waypoints
Costs nil Material/region/link traversal cost

Path.CalculationSecondsTimeout limits how long the solver may run per ComputeAsync call. Set it after CreatePath and before computing.

PathWaypoint actions

Each waypoint has a Position and an Action:

  • Enum.PathWaypointAction.Walk: normal movement.
  • Enum.PathWaypointAction.Jump: trigger jump.
  • Custom labels like "Climb" or "UseBoat" from PathfindingModifiers/Links.

Pathfinding modifiers

PathfindingModifier instances on anchored, non-colliding parts let you influence path cost:

  • Label: key used in Costs table.
  • PassThrough: if true, the volume is ignored by the navmesh and treated as traversable empty space (e.g., zombies "hearing" through doors).

Example:

local path = PathfindingService:CreatePath({
    Costs = {
        Water = 20,
        DangerZone = math.huge,
        UseBoat = 2,
    }
})

Pathfinding links

PathfindingLink connects two Attachments with a custom label and cost, allowing paths across normally untraversable gaps.

Use for:

  • Boats across water
  • Teleporters
  • Ladders
  • One-way jumps

Your movement code checks the waypoint label and runs the custom traversal logic.

Movement patterns

Follow

Continuously recompute a path to a moving target. Throttle recomputation (e.g., every 0.5–1 s) and only recompute if the target moved far enough.

Patrol

Cycle through a list of fixed points. Recompute when blocked.

Chase

Like follow, but validate line-of-sight and distance server-side. Don't trust client-reported positions for authoritative AI.

State machine

Common NPC states: Idle, Patrol, Chase, Attack, Return. Each state handles its own path computation and Humanoid control.

Streaming compatibility

  • Server-side scripts have full world state and can compute paths to any part.
  • Client-side scripts may fail if the destination has streamed out. Use workspace.PersistentLoaded and persistent models for client path destinations.
  • Recompute paths when dynamic/streamed obstacles block the way.

Limitations

  • Direct line-of-sight distance ≤ 3,000 studs.
  • Computation node budget ≈ 20,000 nodes.
  • Waypoint Y coordinate must be between -65,536 and +65,536 studs.
  • Incompatible parameters (e.g., AgentCanJump = false to a jump-only destination) will fail.

Performance at scale

  • Recompute paths on a staggered schedule, not every frame.
  • Share target positions across similar NPCs when possible.
  • Use WaypointSpacing = math.huge to reduce intermediate waypoints for long straight runs.
  • Consider simplifying agent geometry or using fewer active agents.
  • For very large worlds, split into regions or use local patrol paths.

Common mistakes

  • Computing paths every frame.
  • Ignoring blocked-path events and letting NPCs walk into walls.
  • Trusting client position for authoritative AI.
  • Forgetting pcall around ComputeAsync.
  • Using material names incorrectly in Costs (must match Enum.Material names as strings).

Scripts

  • scripts/NPCPathFollower.lua: Humanoid-based path follower with blocked-path recompute, custom-label support, and connection cleanup.
  • scripts/PatrolBehavior.lua: state-driven patrol/chase behavior with spatial detection and throttled recomputation.
  • scripts/PathfindingUtility.lua: helpers for throttled recomputation and waypoint formatting.

Best practices

  • Set Path.CalculationSecondsTimeout after CreatePath to cap solver time.
  • Always set an explicit Humanoid:MoveTo timeout and cancel it when the waypoint is reached or the follower is stopped.
  • Detect targets with spatial queries such as workspace:GetPartBoundsInRadius instead of scanning every player each frame.
  • Stop path followers and clean up Heartbeat connections when the Humanoid dies or the NPC is destroyed.
  • For respawning NPCs, create a new behavior instance for the new character model and Destroy the old one.
  • Use PathfindingLink labels to trigger custom traversal logic (boats, teleporters, ladders). The follower invokes a registered handler; if none exists, the waypoint falls back to normal movement.
  • To enable climbing, set AgentCanClimb = true and provide TrussPart surfaces. Climb waypoints have the Label "Climb".
  • PathfindingModifier parts must be Anchored = true and CanCollide = false.

How to proceed

  1. Define the agent's size and movement abilities.
  2. Build the world with modifiers/links for special regions.
  3. Implement a path-follower that handles waypoints, jumps, and blocked events.
  4. Layer a state machine for complex behaviors.
  5. Run on the server for authoritative AI; use client only for visual prediction.
  6. Profile with MicroProfiler and stagger recomputation for many agents.

Reference index

Files (roblox-suite)
  • references
    • modifiers-links-and-streaming.md 2.8 KB
      ---
      read_when: "Route NPCs through special regions or handle streamed geometry"
      last_reviewed: 2026-06-17
      ---
      
      # Modifiers, links, and streaming
      
      Official guide: https://create.roblox.com/docs/en-us/characters/pathfinding
      
      ## PathfindingModifier
      
      A `PathfindingModifier` is an instance placed on an anchored, non-colliding part to influence path cost or traversability.
      
      Properties:
      - `Label`: string key referenced in `CreatePath` `Costs`.
      - `PassThrough`: if `true`, the volume is ignored by the navmesh and treated as traversable empty space. The pathfinder can route straight through it; your NPC code is responsible for actually opening the door, climbing, etc.
      
      ### Region modifier example
      
      To mark a dangerous zone that NPCs should avoid:
      
      1. Create an anchored part covering the region.
      2. Set `CanCollide = false`.
      3. Add a `PathfindingModifier` with `Label = "DangerZone"`.
      4. In `CreatePath`:
      
      ```lua
      Costs = {
          DangerZone = math.huge,
      }
      ```
      
      ### Pass-through example
      
      To let a zombie path through a closed door it cannot actually open:
      
      1. Create an anchored part covering the door.
      2. Set `CanCollide = false`.
      3. Add a `PathfindingModifier` with `PassThrough = true`.
      
      The path will route through the door; your NPC code can then play an animation or sound.
      
      ## PathfindingLink
      
      Links connect two `Attachment`s and allow traversal that the navmesh would not normally support.
      
      Setup:
      1. Create `Attachment0` and `Attachment1` on different parts.
      2. Create a `PathfindingLink` in workspace.
      3. Set `Attachment0`, `Attachment1`, and `Label`.
      4. Optionally set `IsBidirectional`.
      
      ```lua
      local link = Instance.new("PathfindingLink")
      link.Attachment0 = attachmentA
      link.Attachment1 = attachmentB
      link.Label = "UseBoat"
      link.IsBidirectional = true
      link.Parent = workspace
      ```
      
      In your pathfinder:
      
      ```lua
      Costs = {
          Water = 20,
          UseBoat = 2,
      }
      ```
      
      When the waypoint label is `"UseBoat"`, trigger your custom action, then continue along the path:
      
      ```lua
      local function onUseBoat(agent, waypoint)
          local boat = findBoatNear(waypoint.Position)
          seatAgent(agent, boat)
          moveBoat(boat, waypoint.Position)
          unseatAgent(agent)
      end
      
      -- In your path follower:
      if waypoint.Label == "UseBoat" then
          onUseBoat(agent, waypoint)
      end
      ```
      
      ## Streaming compatibility
      
      Server-side pathfinding is unaffected by streaming.
      
      Client-side pathfinding:
      - Compute paths to persistent models when possible.
      - Listen for `workspace.PersistentLoaded`.
      - Recompute paths if streamed obstacles block the way.
      
      ```lua
      workspace.PersistentLoaded:Connect(function(persistentModel)
          if persistentModel.Name == "ImportantDestination" then
              -- safe to reference now
          end
      end)
      ```
      
      ## Limitations recap
      
      - Max direct distance: 3,000 studs.
      - Max nodes: ~20,000.
      - Vertical waypoint range: ±65,536 studs.
      - Parameter incompatibility causes failures.
      
    • npc-behavior-patterns.md 4.1 KB
      ---
      read_when: "Implement patrol, chase, combat, or state transitions"
      last_reviewed: 2026-06-17
      ---
      
      # NPC behavior patterns
      
      ## State machine
      
      A state machine is the most maintainable pattern for NPCs:
      
      ```
      Idle → Patrol → Alert → Chase → Attack → Return
      ```
      
      Each state:
      - Decides when to transition.
      - Controls the Humanoid or mover constraints.
      - Manages its own path computation.
      
      ## Follow behavior
      
      Recompute path to a moving target on a staggered interval:
      
      ```lua
      local lastRecompute = 0
      local RECOMPUTE_INTERVAL = 0.5
      local MIN_MOVE_DIST = 5
      
      RunService.Heartbeat:Connect(function()
          local now = time()
          if now - lastRecompute < RECOMPUTE_INTERVAL then return end
      
          local hrp = target:FindFirstChild("HumanoidRootPart")
          if not hrp then return end
          if (hrp.Position - lastTargetPos).Magnitude < MIN_MOVE_DIST then return end
      
          lastRecompute = now
          lastTargetPos = hrp.Position
          followPath(hrp.Position)
      end)
      ```
      
      ## Spatial target detection
      
      Instead of scanning every player each frame, use a spatial query and validate the result:
      
      ```lua
      local function findTarget(rootPart, detectionRange)
          local characters = {}
          for _, player in ipairs(Players:GetPlayers()) do
              if player.Character then
                  table.insert(characters, player.Character)
              end
          end
          if #characters == 0 then return nil end
      
          local params = OverlapParams.new()
          params.FilterDescendantsInstances = characters
          params.FilterType = Enum.RaycastFilterType.Whitelist
      
          local parts = workspace:GetPartBoundsInRadius(rootPart.Position, detectionRange, params)
          for _, part in ipairs(parts) do
              local character = part:FindFirstAncestorOfClass("Model")
              if not character then continue end
              local hrp = character:FindFirstChild("HumanoidRootPart")
              local humanoid = character:FindFirstChild("Humanoid")
              if hrp and humanoid and humanoid.Health > 0 then
                  return character
              end
          end
          return nil
      end
      ```
      
      ## Patrol behavior
      
      Loop through waypoints. Recompute path between points if blocked.
      
      ## Chase behavior
      
      Server-authoritative chase:
      - Detect target on server (line of sight, distance).
      - Recompute path toward server-known target position.
      - Validate target reach server-side before triggering attack.
      
      Never trust a client that says "I am here, hit me."
      
      ## Group behavior
      
      For groups of NPCs:
      - Share a target position among squad members.
      - Use formation offsets so they don't stack.
      - Stagger recomputation to avoid spikes.
      
      ## Death and respawn handling
      
      Stop all pathfinding and AI updates when the NPC dies:
      
      ```lua
      humanoid.Died:Connect(function()
          behavior:Destroy()
      end)
      ```
      
      When an NPC respawns with a new character model, create a fresh behavior instance for that model and `Destroy` the old one. Do not reuse followers across models because connections and `Humanoid` references become stale.
      
      ## Cleanup / destroy pattern
      
      Every behavior that connects to `RunService.Heartbeat`, `Humanoid` events, or `Path.Blocked` should expose a `Destroy` method that disconnects everything and releases references:
      
      ```lua
      function Behavior:Destroy()
          if self.connection then
              self.connection:Disconnect()
              self.connection = nil
          end
          if self.follower then
              self.follower:Destroy()
              self.follower = nil
          end
          self.model = nil
          self.humanoid = nil
      end
      ```
      
      ## Non-humanoid agents
      
      For agents without a `Humanoid`, use `AlignPosition`/`AlignOrientation` or `LinearVelocity` to move a root part along waypoints. See [roblox-physics/SKILL.md](../../roblox-physics/SKILL.md).
      
      ## Humanoid tuning
      
      - `Humanoid.WalkSpeed` controls movement speed.
      - `Humanoid.JumpPower` / `Humanoid.JumpHeight` control jumps.
      - Call `Humanoid:Move(Vector3)` for direction-based movement.
      - Use `Humanoid:MoveTo(position)` for simple point-to-point movement without pathfinding.
      
      ## Common anti-patterns
      
      - Running pathfinding on every Heartbeat.
      - Moving NPCs on the client and trusting their position.
      - Ignoring `Blocked` events.
      - Spawning too many agents without staggering.
      - Using `Humanoid:MoveTo` for long-distance navigation without pathfinding.
      
    • pathfinding-service-details.md 3.6 KB
      ---
      read_when: "Configure agents, compute paths, or handle blocked waypoints"
      last_reviewed: 2026-06-17
      ---
      
      # Pathfinding service details
      
      Official guide: https://create.roblox.com/docs/en-us/characters/pathfinding
      
      ## Creating a path
      
      ```lua
      local PathfindingService = game:GetService("PathfindingService")
      
      local path = PathfindingService:CreatePath({
          AgentRadius = 2,
          AgentHeight = 5,
          AgentCanJump = true,
          AgentCanClimb = false,
          WaypointSpacing = 4,
          Costs = {
              Water = 20,
              CrackedLava = 100,
              DangerZone = math.huge,
          }
      })
      path.CalculationSecondsTimeout = 1
      ```
      
      ## Computing the path
      
      Set a solver timeout after creating the path, then wrap `ComputeAsync` in `pcall`:
      
      ```lua
      path.CalculationSecondsTimeout = 1
      
      local rootPart = character:WaitForChild("HumanoidRootPart")
      local ok, err = pcall(function()
          path:ComputeAsync(rootPart.Position, endPos)
      end)
      
      if not ok or path.Status ~= Enum.PathStatus.Success then
          warn("Path computation failed:", err or path.Status)
          return nil
      end
      
      local waypoints = path:GetWaypoints()
      ```
      
      ## PathWaypoint structure
      
      ```lua
      for i, waypoint in ipairs(waypoints) do
          print(i, waypoint.Position, waypoint.Action, waypoint.Label)
      end
      ```
      
      - `Position`: `Vector3` target.
      - `Action`: `Enum.PathWaypointAction`.
      - `Label`: custom string from modifiers/links.
      
      ## Blocked paths
      
      Connect to `path.Blocked` and only recompute if the blocked waypoint is ahead. Disconnect the old connection before recomputing to avoid duplicate listeners:
      
      ```lua
      local blockedConnection
      
      local function followPath(targetPosition)
          -- compute path, get waypoints, currentWaypointIndex ...
      
          if blockedConnection then
              blockedConnection:Disconnect()
              blockedConnection = nil
          end
      
          blockedConnection = path.Blocked:Connect(function(blockedIndex)
              if blockedIndex >= currentWaypointIndex then
                  blockedConnection:Disconnect()
                  blockedConnection = nil
                  followPath(targetPosition)
              end
          end)
      end
      ```
      
      ## MoveTo timeout and cancellation
      
      `Humanoid:MoveTo` has an implicit timeout. Set your own explicit timeout, cancel it when `MoveToFinished` fires, and retry or fail after repeated misses:
      
      ```lua
      local moveToTimeoutThread
      
      local function moveToWaypoint(waypoint)
          humanoid:MoveTo(waypoint.Position)
      
          if moveToTimeoutThread then
              task.cancel(moveToTimeoutThread)
          end
          moveToTimeoutThread = task.delay(6, function()
              -- retry or report failure
              moveToWaypoint(waypoint)
          end)
      end
      
      humanoid.MoveToFinished:Connect(function(reached)
          if moveToTimeoutThread then
              task.cancel(moveToTimeoutThread)
              moveToTimeoutThread = nil
          end
          if reached then
              -- advance to next waypoint
          end
      end)
      ```
      
      ## Climbing
      
      Set `AgentCanClimb = true` in `CreatePath` to allow routes over `TrussPart` surfaces. Climb waypoints have `Action == Enum.PathWaypointAction.Climb` and `Label == "Climb"`. The Humanoid performs the climb automatically when it reaches the truss.
      
      ## Material costs
      
      Keys are strings matching `Enum.Material` names:
      
      ```lua
      Costs = {
          Water = 20,
          CrackedLava = 100,
          Slate = 20,
      }
      ```
      
      Use `math.huge` to forbid traversal entirely.
      
      ## Common statuses
      
      - `Enum.PathStatus.Success`: path found.
      - `Enum.PathStatus.NoPath`: no valid path with given parameters.
      - `Enum.PathStatus.ClosestNoPath`: partial path returned to nearest reachable point.
      
      ## Debugging
      
      Enable in Studio Visualization Options:
      - **Navigation mesh**
      - **Pathfinding modifiers**
      - **Pathfinding links**
      
      These show traversable areas, modifier labels, and link connections.
      
    • performance-and-scaling.md 2 KB
      ---
      read_when: "Reduce pathfinding and simulation cost as NPC counts grow"
      last_reviewed: 2026-06-17
      ---
      
      # Performance and scaling
      
      ## Throttle pathfinding
      
      Avoid recomputing every frame. Use:
      - Time interval (e.g., every 0.5–1 s).
      - Distance threshold (only recompute if target moved > N studs).
      - Event-driven recomputation (on `path.Blocked`).
      
      ## Reduce waypoint count
      
      Set `WaypointSpacing = math.huge` to eliminate intermediate waypoints when straight-line movement is acceptable.
      
      ## Batch agents
      
      Distribute recomputation across frames using a time budget so a large population cannot stall the frame:
      
      ```lua
      local RunService = game:GetService("RunService")
      local agents = {}
      local index = 1
      local BUDGET_MS = 2
      
      RunService.Heartbeat:Connect(function()
          if #agents == 0 then return end
          local start = os.clock()
          local processed = 0
          repeat
              local agent = agents[index]
              agent:think()
              index = (index % #agents) + 1
              processed += 1
          until processed >= #agents or (os.clock() - start) * 1000 >= BUDGET_MS
      end)
      ```
      
      ## Use local patrol paths
      
      For large worlds, split the map into regions. NPCs pick patrol paths within their current region unless chasing a target.
      
      ## Avoid pathfinding when unnecessary
      
      If the target is close and visible, move directly. Use raycasts for simple line-of-sight checks.
      
      ## Cache paths
      
      If many agents share the same destination, compute once and share waypoints.
      
      ## Spatial queries for target detection
      
      Use `workspace:GetPartBoundsInRadius` with an `OverlapParams` whitelist of player characters instead of iterating every player each frame. Validate hits with `FindFirstChild("HumanoidRootPart")` and `Humanoid.Health` checks.
      
      ## MicroProfiler
      
      Tag AI work with `debug.profilebegin`/`debug.profileend`:
      
      ```lua
      debug.profilebegin("NPC Think")
      -- pathfinding and state logic
      debug.profileend()
      ```
      
      ## Limits to keep in mind
      
      - Max path distance: 3,000 studs.
      - Max nodes: ~20,000.
      - Each `ComputeAsync` is a solver call; treat it as a budgeted operation.
      
  • scripts
    • NPCPathFollower.lua 9.6 KB · in bundle
    • PathfindingUtility.lua 1.4 KB · in bundle
    • PatrolBehavior.lua 10.3 KB · in bundle
  • SKILL.md 7.1 KB
    ---
    name: roblox-npcs
    description: "Build or debug Roblox NPC navigation and behavior. Use for PathfindingService, modifiers and links, blocked paths, patrol or chase logic, streaming, and scaling NPC counts."
    last_reviewed: 2026-06-17
    ---
    
    # roblox-npcs
    
    **Official sources:**
    - https://create.roblox.com/docs/en-us/characters/pathfinding
    - https://create.roblox.com/docs/en-us/workspace/streaming
    - Engine classes: `PathfindingService`, `Path`, `PathWaypoint`, `PathfindingModifier`, `PathfindingLink`, `Humanoid`
    
    Use PathfindingService for navigation on Roblox's navigation mesh. Build patrol, chase, and other behavior around its computed paths.
    
    ## PathfindingService basics
    
    Create a path:
    
    ```lua
    local PathfindingService = game:GetService("PathfindingService")
    
    local path = PathfindingService:CreatePath({
        AgentRadius = 2,
        AgentHeight = 5,
        AgentCanJump = true,
        AgentCanClimb = false,
        WaypointSpacing = 4,
        Costs = {
            Water = 20,
            DangerZone = math.huge,
        }
    })
    path.CalculationSecondsTimeout = 1
    ```
    
    Compute and follow:
    
    ```lua
    local humanoid = character:WaitForChild("Humanoid")
    local rootPart = character:WaitForChild("HumanoidRootPart")
    
    local success, err = pcall(function()
        path:ComputeAsync(rootPart.Position, endPos)
    end)
    
    if success and path.Status == Enum.PathStatus.Success then
        local waypoints = path:GetWaypoints()
        -- follow waypoints with Humanoid:Move()
    end
    ```
    
    ## Agent parameters
    
    | Parameter | Default | Purpose |
    | --- | --- | --- |
    | `AgentRadius` | 2 studs | Minimum clearance from obstacles |
    | `AgentHeight` | 5 studs | Vertical clearance |
    | `AgentCanJump` | true | Allows jump waypoints |
    | `AgentCanClimb` | false | Allows climbing truss parts |
    | `WaypointSpacing` | 4 studs | Distance between intermediate waypoints |
    | `Costs` | nil | Material/region/link traversal cost |
    
    `Path.CalculationSecondsTimeout` limits how long the solver may run per `ComputeAsync` call. Set it after `CreatePath` and before computing.
    
    ## PathWaypoint actions
    
    Each waypoint has a `Position` and an `Action`:
    - `Enum.PathWaypointAction.Walk`: normal movement.
    - `Enum.PathWaypointAction.Jump`: trigger jump.
    - Custom labels like `"Climb"` or `"UseBoat"` from PathfindingModifiers/Links.
    
    ## Pathfinding modifiers
    
    `PathfindingModifier` instances on anchored, non-colliding parts let you influence path cost:
    - `Label`: key used in `Costs` table.
    - `PassThrough`: if `true`, the volume is ignored by the navmesh and treated as traversable empty space (e.g., zombies "hearing" through doors).
    
    Example:
    
    ```lua
    local path = PathfindingService:CreatePath({
        Costs = {
            Water = 20,
            DangerZone = math.huge,
            UseBoat = 2,
        }
    })
    ```
    
    ## Pathfinding links
    
    `PathfindingLink` connects two `Attachment`s with a custom label and cost, allowing paths across normally untraversable gaps.
    
    Use for:
    - Boats across water
    - Teleporters
    - Ladders
    - One-way jumps
    
    Your movement code checks the waypoint label and runs the custom traversal logic.
    
    ## Movement patterns
    
    ### Follow
    
    Continuously recompute a path to a moving target. Throttle recomputation (e.g., every 0.5–1 s) and only recompute if the target moved far enough.
    
    ### Patrol
    
    Cycle through a list of fixed points. Recompute when blocked.
    
    ### Chase
    
    Like follow, but validate line-of-sight and distance server-side. Don't trust client-reported positions for authoritative AI.
    
    ### State machine
    
    Common NPC states: Idle, Patrol, Chase, Attack, Return. Each state handles its own path computation and Humanoid control.
    
    ## Streaming compatibility
    
    - Server-side scripts have full world state and can compute paths to any part.
    - Client-side scripts may fail if the destination has streamed out. Use `workspace.PersistentLoaded` and persistent models for client path destinations.
    - Recompute paths when dynamic/streamed obstacles block the way.
    
    ## Limitations
    
    - Direct line-of-sight distance ≤ 3,000 studs.
    - Computation node budget ≈ 20,000 nodes.
    - Waypoint Y coordinate must be between -65,536 and +65,536 studs.
    - Incompatible parameters (e.g., `AgentCanJump = false` to a jump-only destination) will fail.
    
    ## Performance at scale
    
    - Recompute paths on a staggered schedule, not every frame.
    - Share target positions across similar NPCs when possible.
    - Use `WaypointSpacing = math.huge` to reduce intermediate waypoints for long straight runs.
    - Consider simplifying agent geometry or using fewer active agents.
    - For very large worlds, split into regions or use local patrol paths.
    
    ## Common mistakes
    
    - Computing paths every frame.
    - Ignoring blocked-path events and letting NPCs walk into walls.
    - Trusting client position for authoritative AI.
    - Forgetting `pcall` around `ComputeAsync`.
    - Using material names incorrectly in `Costs` (must match `Enum.Material` names as strings).
    
    ## Scripts
    
    - `scripts/NPCPathFollower.lua`: Humanoid-based path follower with blocked-path recompute, custom-label support, and connection cleanup.
    - `scripts/PatrolBehavior.lua`: state-driven patrol/chase behavior with spatial detection and throttled recomputation.
    - `scripts/PathfindingUtility.lua`: helpers for throttled recomputation and waypoint formatting.
    
    ## Best practices
    
    - Set `Path.CalculationSecondsTimeout` after `CreatePath` to cap solver time.
    - Always set an explicit `Humanoid:MoveTo` timeout and cancel it when the waypoint is reached or the follower is stopped.
    - Detect targets with spatial queries such as `workspace:GetPartBoundsInRadius` instead of scanning every player each frame.
    - Stop path followers and clean up `Heartbeat` connections when the `Humanoid` dies or the NPC is destroyed.
    - For respawning NPCs, create a new behavior instance for the new character model and `Destroy` the old one.
    - Use `PathfindingLink` labels to trigger custom traversal logic (boats, teleporters, ladders). The follower invokes a registered handler; if none exists, the waypoint falls back to normal movement.
    - To enable climbing, set `AgentCanClimb = true` and provide `TrussPart` surfaces. Climb waypoints have the `Label` `"Climb"`.
    - `PathfindingModifier` parts must be `Anchored = true` and `CanCollide = false`.
    
    ## How to proceed
    
    1. Define the agent's size and movement abilities.
    2. Build the world with modifiers/links for special regions.
    3. Implement a path-follower that handles waypoints, jumps, and blocked events.
    4. Layer a state machine for complex behaviors.
    5. Run on the server for authoritative AI; use client only for visual prediction.
    6. Profile with MicroProfiler and stagger recomputation for many agents.
    
    <!-- catalog:references:start -->
    ## Reference index
    
    - [modifiers-links-and-streaming.md](references/modifiers-links-and-streaming.md): Route NPCs through special regions or handle streamed geometry.
    - [npc-behavior-patterns.md](references/npc-behavior-patterns.md): Implement patrol, chase, combat, or state transitions.
    - [pathfinding-service-details.md](references/pathfinding-service-details.md): Configure agents, compute paths, or handle blocked waypoints.
    - [performance-and-scaling.md](references/performance-and-scaling.md): Reduce pathfinding and simulation cost as NPC counts grow.
    <!-- catalog:references:end -->
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related