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.
Install
npx skills add https://github.com/nonlooped/roblox-suite/tree/main/roblox-npcs
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install nonlooped-roblox-suite@llmmart
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:
- 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:
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 inCoststable.PassThrough: iftrue, 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.PersistentLoadedand 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 = falseto 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.hugeto 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
pcallaroundComputeAsync. - Using material names incorrectly in
Costs(must matchEnum.Materialnames 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.CalculationSecondsTimeoutafterCreatePathto cap solver time. - Always set an explicit
Humanoid:MoveTotimeout and cancel it when the waypoint is reached or the follower is stopped. - Detect targets with spatial queries such as
workspace:GetPartBoundsInRadiusinstead of scanning every player each frame. - Stop path followers and clean up
Heartbeatconnections when theHumanoiddies or the NPC is destroyed. - For respawning NPCs, create a new behavior instance for the new character model and
Destroythe old one. - Use
PathfindingLinklabels 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 = trueand provideTrussPartsurfaces. Climb waypoints have theLabel"Climb". PathfindingModifierparts must beAnchored = trueandCanCollide = false.
How to proceed
- Define the agent's size and movement abilities.
- Build the world with modifiers/links for special regions.
- Implement a path-follower that handles waypoints, jumps, and blocked events.
- Layer a state machine for complex behaviors.
- Run on the server for authoritative AI; use client only for visual prediction.
- Profile with MicroProfiler and stagger recomputation for many agents.
Reference index
- modifiers-links-and-streaming.md: Route NPCs through special regions or handle streamed geometry.
- npc-behavior-patterns.md: Implement patrol, chase, combat, or state transitions.
- pathfinding-service-details.md: Configure agents, compute paths, or handle blocked waypoints.
- performance-and-scaling.md: Reduce pathfinding and simulation cost as NPC counts grow.
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.
Reviews (0)
No reviews yet.
No comments yet.