{"slug":"roblox-npcs","title":"roblox-npcs","summary":"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.","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-10-18T17:23:56.958612Z","repo":{"url":"https://github.com/nonlooped/roblox-suite","stars":22,"forks":0,"license":"MIT","updatedAt":"2026-10-17T23:48:49Z"},"bodyHtml":"<hr>\n<h2>name: roblox-npcs\ndescription: \"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.\"\nlast_reviewed: 2026-06-17</h2>\n<h1>roblox-npcs</h1>\n<p><strong>Official sources:</strong></p>\n<ul>\n<li><a href=\"https://create.roblox.com/docs/en-us/characters/pathfinding\">https://create.roblox.com/docs/en-us/characters/pathfinding</a></li>\n<li><a href=\"https://create.roblox.com/docs/en-us/workspace/streaming\">https://create.roblox.com/docs/en-us/workspace/streaming</a></li>\n<li>Engine classes: <code>PathfindingService</code>, <code>Path</code>, <code>PathWaypoint</code>, <code>PathfindingModifier</code>, <code>PathfindingLink</code>, <code>Humanoid</code></li>\n</ul>\n<p>Use PathfindingService for navigation on Roblox's navigation mesh. Build patrol, chase, and other behavior around its computed paths.</p>\n<h2>PathfindingService basics</h2>\n<p>Create a path:</p>\n<pre><code>local PathfindingService = game:GetService(\"PathfindingService\")\n\nlocal path = PathfindingService:CreatePath({\n    AgentRadius = 2,\n    AgentHeight = 5,\n    AgentCanJump = true,\n    AgentCanClimb = false,\n    WaypointSpacing = 4,\n    Costs = {\n        Water = 20,\n        DangerZone = math.huge,\n    }\n})\npath.CalculationSecondsTimeout = 1\n</code></pre>\n<p>Compute and follow:</p>\n<pre><code>local humanoid = character:WaitForChild(\"Humanoid\")\nlocal rootPart = character:WaitForChild(\"HumanoidRootPart\")\n\nlocal success, err = pcall(function()\n    path:ComputeAsync(rootPart.Position, endPos)\nend)\n\nif success and path.Status == Enum.PathStatus.Success then\n    local waypoints = path:GetWaypoints()\n    -- follow waypoints with Humanoid:Move()\nend\n</code></pre>\n<h2>Agent parameters</h2>\n<table>\n<thead>\n<tr>\n<th>Parameter</th>\n<th>Default</th>\n<th>Purpose</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>AgentRadius</code></td>\n<td>2 studs</td>\n<td>Minimum clearance from obstacles</td>\n</tr>\n<tr>\n<td><code>AgentHeight</code></td>\n<td>5 studs</td>\n<td>Vertical clearance</td>\n</tr>\n<tr>\n<td><code>AgentCanJump</code></td>\n<td>true</td>\n<td>Allows jump waypoints</td>\n</tr>\n<tr>\n<td><code>AgentCanClimb</code></td>\n<td>false</td>\n<td>Allows climbing truss parts</td>\n</tr>\n<tr>\n<td><code>WaypointSpacing</code></td>\n<td>4 studs</td>\n<td>Distance between intermediate waypoints</td>\n</tr>\n<tr>\n<td><code>Costs</code></td>\n<td>nil</td>\n<td>Material/region/link traversal cost</td>\n</tr>\n</tbody>\n</table>\n<p><code>Path.CalculationSecondsTimeout</code> limits how long the solver may run per <code>ComputeAsync</code> call. Set it after <code>CreatePath</code> and before computing.</p>\n<h2>PathWaypoint actions</h2>\n<p>Each waypoint has a <code>Position</code> and an <code>Action</code>:</p>\n<ul>\n<li><code>Enum.PathWaypointAction.Walk</code>: normal movement.</li>\n<li><code>Enum.PathWaypointAction.Jump</code>: trigger jump.</li>\n<li>Custom labels like <code>\"Climb\"</code> or <code>\"UseBoat\"</code> from PathfindingModifiers/Links.</li>\n</ul>\n<h2>Pathfinding modifiers</h2>\n<p><code>PathfindingModifier</code> instances on anchored, non-colliding parts let you influence path cost:</p>\n<ul>\n<li><code>Label</code>: key used in <code>Costs</code> table.</li>\n<li><code>PassThrough</code>: if <code>true</code>, the volume is ignored by the navmesh and treated as traversable empty space (e.g., zombies \"hearing\" through doors).</li>\n</ul>\n<p>Example:</p>\n<pre><code>local path = PathfindingService:CreatePath({\n    Costs = {\n        Water = 20,\n        DangerZone = math.huge,\n        UseBoat = 2,\n    }\n})\n</code></pre>\n<h2>Pathfinding links</h2>\n<p><code>PathfindingLink</code> connects two <code>Attachment</code>s with a custom label and cost, allowing paths across normally untraversable gaps.</p>\n<p>Use for:</p>\n<ul>\n<li>Boats across water</li>\n<li>Teleporters</li>\n<li>Ladders</li>\n<li>One-way jumps</li>\n</ul>\n<p>Your movement code checks the waypoint label and runs the custom traversal logic.</p>\n<h2>Movement patterns</h2>\n<h3>Follow</h3>\n<p>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.</p>\n<h3>Patrol</h3>\n<p>Cycle through a list of fixed points. Recompute when blocked.</p>\n<h3>Chase</h3>\n<p>Like follow, but validate line-of-sight and distance server-side. Don't trust client-reported positions for authoritative AI.</p>\n<h3>State machine</h3>\n<p>Common NPC states: Idle, Patrol, Chase, Attack, Return. Each state handles its own path computation and Humanoid control.</p>\n<h2>Streaming compatibility</h2>\n<ul>\n<li>Server-side scripts have full world state and can compute paths to any part.</li>\n<li>Client-side scripts may fail if the destination has streamed out. Use <code>workspace.PersistentLoaded</code> and persistent models for client path destinations.</li>\n<li>Recompute paths when dynamic/streamed obstacles block the way.</li>\n</ul>\n<h2>Limitations</h2>\n<ul>\n<li>Direct line-of-sight distance ≤ 3,000 studs.</li>\n<li>Computation node budget ≈ 20,000 nodes.</li>\n<li>Waypoint Y coordinate must be between -65,536 and +65,536 studs.</li>\n<li>Incompatible parameters (e.g., <code>AgentCanJump = false</code> to a jump-only destination) will fail.</li>\n</ul>\n<h2>Performance at scale</h2>\n<ul>\n<li>Recompute paths on a staggered schedule, not every frame.</li>\n<li>Share target positions across similar NPCs when possible.</li>\n<li>Use <code>WaypointSpacing = math.huge</code> to reduce intermediate waypoints for long straight runs.</li>\n<li>Consider simplifying agent geometry or using fewer active agents.</li>\n<li>For very large worlds, split into regions or use local patrol paths.</li>\n</ul>\n<h2>Common mistakes</h2>\n<ul>\n<li>Computing paths every frame.</li>\n<li>Ignoring blocked-path events and letting NPCs walk into walls.</li>\n<li>Trusting client position for authoritative AI.</li>\n<li>Forgetting <code>pcall</code> around <code>ComputeAsync</code>.</li>\n<li>Using material names incorrectly in <code>Costs</code> (must match <code>Enum.Material</code> names as strings).</li>\n</ul>\n<h2>Scripts</h2>\n<ul>\n<li><code>scripts/NPCPathFollower.lua</code>: Humanoid-based path follower with blocked-path recompute, custom-label support, and connection cleanup.</li>\n<li><code>scripts/PatrolBehavior.lua</code>: state-driven patrol/chase behavior with spatial detection and throttled recomputation.</li>\n<li><code>scripts/PathfindingUtility.lua</code>: helpers for throttled recomputation and waypoint formatting.</li>\n</ul>\n<h2>Best practices</h2>\n<ul>\n<li>Set <code>Path.CalculationSecondsTimeout</code> after <code>CreatePath</code> to cap solver time.</li>\n<li>Always set an explicit <code>Humanoid:MoveTo</code> timeout and cancel it when the waypoint is reached or the follower is stopped.</li>\n<li>Detect targets with spatial queries such as <code>workspace:GetPartBoundsInRadius</code> instead of scanning every player each frame.</li>\n<li>Stop path followers and clean up <code>Heartbeat</code> connections when the <code>Humanoid</code> dies or the NPC is destroyed.</li>\n<li>For respawning NPCs, create a new behavior instance for the new character model and <code>Destroy</code> the old one.</li>\n<li>Use <code>PathfindingLink</code> 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.</li>\n<li>To enable climbing, set <code>AgentCanClimb = true</code> and provide <code>TrussPart</code> surfaces. Climb waypoints have the <code>Label</code> <code>\"Climb\"</code>.</li>\n<li><code>PathfindingModifier</code> parts must be <code>Anchored = true</code> and <code>CanCollide = false</code>.</li>\n</ul>\n<h2>How to proceed</h2>\n<ol>\n<li>Define the agent's size and movement abilities.</li>\n<li>Build the world with modifiers/links for special regions.</li>\n<li>Implement a path-follower that handles waypoints, jumps, and blocked events.</li>\n<li>Layer a state machine for complex behaviors.</li>\n<li>Run on the server for authoritative AI; use client only for visual prediction.</li>\n<li>Profile with MicroProfiler and stagger recomputation for many agents.</li>\n</ol>\n\n<h2>Reference index</h2>\n<ul>\n<li><a href=\"references/modifiers-links-and-streaming.md\">modifiers-links-and-streaming.md</a>: Route NPCs through special regions or handle streamed geometry.</li>\n<li><a href=\"references/npc-behavior-patterns.md\">npc-behavior-patterns.md</a>: Implement patrol, chase, combat, or state transitions.</li>\n<li><a href=\"references/pathfinding-service-details.md\">pathfinding-service-details.md</a>: Configure agents, compute paths, or handle blocked waypoints.</li>\n<li><a href=\"references/performance-and-scaling.md\">performance-and-scaling.md</a>: Reduce pathfinding and simulation cost as NPC counts grow.</li>\n</ul>\n\n","files":[{"path":"references/modifiers-links-and-streaming.md","sizeBytes":2862,"isText":true},{"path":"references/npc-behavior-patterns.md","sizeBytes":4194,"isText":true},{"path":"references/pathfinding-service-details.md","sizeBytes":3673,"isText":true},{"path":"references/performance-and-scaling.md","sizeBytes":2045,"isText":true},{"path":"scripts/NPCPathFollower.lua","sizeBytes":9852,"isText":false},{"path":"scripts/PathfindingUtility.lua","sizeBytes":1475,"isText":false},{"path":"scripts/PatrolBehavior.lua","sizeBytes":10499,"isText":false},{"path":"SKILL.md","sizeBytes":7224,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-10-18T17:24:18.414266Z","sha256":"E8186586EFA4F7E8BF02F4C1DA3099A59356B6F8F9314463B521CF15C1A28D7E","sizeBytes":14746},"review":null,"source":{"repositoryUrl":"https://github.com/nonlooped/roblox-suite","path":"roblox-npcs","license":"MIT","commit":"2904c1660c702d82f1ab67f46ddf619d6abc9a2d","subtreeSha":"C11197C7F3768E87C8EBDF1BF06EA3DC7127B2A3BF3D9DD89F29E844631BF538","lastSyncedAt":"2026-10-18T17:23:55.435396Z"},"reviewedAt":"2026-10-18T17:24:43.76925Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/nonlooped/roblox-suite/tree/main/roblox-npcs"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install nonlooped-roblox-suite@llmmart"},{"target":"git","command":"git clone https://github.com/nonlooped/roblox-suite.git"}]}