roblox-npc-ai
Skill92 starsChanged 48 days agoMITView on GitHub
NPCs and enemy AI - PathfindingService (CreatePath, ComputeAsync, costs, modifiers, links, blocked paths), chase/follow loops, state machines, perception and line of sight, spawning/pooling, scaling to many NPCs. Use when creating enemies, pets, followers, shopkeepers, wave spawners, or fixing NPCs that get stuck, jitter, or lag the server.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- TabooHarmony/roblox-braintheir own version, first seen Sep 23, 2026
- szlay/roblox-basetheir own version, first seen Jul 24, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- TabooHarmony/roblox-brain★ 150same name
- szlay/roblox-base★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository25 other files it gives its agents.
- AGENTS.mdAGENTS.md
- docs/agents.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- roblox-analytics-liveopsSkill
- roblox-architectureSkill
- roblox-audioSkill
- roblox-characters-animationSkill
- roblox-code-reviewSkill
- roblox-data-storesSkill
- roblox-game-systemsSkill
- roblox-inputSkill
- roblox-luauSkill
- roblox-monetizationSkill
- roblox-networkingSkill
- roblox-open-cloudSkill
- roblox-performanceSkill
- roblox-physicsSkill
- roblox-securitySkill
- roblox-studio-mcpSkill
- roblox-teleport-matchmakingSkill
- roblox-testingSkill
- roblox-text-chatSkill
- roblox-toolingSkill
- roblox-uiSkill
- roblox-world-buildingSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
Use Humanoid for NPCs that need walking/jumping/climbing.
- Very specific: line 168
Add reaction delays and field-of-view checks (root.CFrame.LookVector:Dot(direction.Unit) > 0.5) so NPCs feel fair.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
NPCs and AI
Architecture
- NPC logic runs on the server (authoritative targets, damage, drops). Visual extras (idle animations, nameplates, effects) can run on clients.
- One NPC service drives all NPCs from a single loop (e.g. 5–10 Hz "think" rate, staggered), not one
script +
while trueper NPC. Tag NPC models (CollectionService) and keep per-NPC state in a table. - Server-owned physics:
npc.PrimaryPart:SetNetworkOwner(nil)after spawning, so NPCs don't stutter when ownership moves between nearby players (and exploiters can't push them). - Use
Humanoidfor NPCs that need walking/jumping/climbing; for simple movers (turrets, floating enemies, crowds) use anAnimationController+Animatorand move with constraints or CFrames.
Pathfinding
--!strict
local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath({
AgentRadius = 2,
AgentHeight = 5,
AgentCanJump = true,
AgentCanClimb = false,
WaypointSpacing = 4,
Costs = {
Water = 20, -- avoid water (Enum.Material name)
DangerZone = math.huge, -- PathfindingModifier.Label: never cross
},
})
-- Walk `humanoid` to `goal`. Returns true on arrival, false if unreachable or interrupted.
local function walkTo(humanoid: Humanoid, goal: Vector3): boolean
local root = humanoid.RootPart
if not root then
return false
end
local ok = pcall(function()
path:ComputeAsync(root.Position, goal)
end)
if not ok or path.Status ~= Enum.PathStatus.Success then
return false -- NoPath: try a fallback (direct MoveTo, new goal, teleport if stuck long)
end
local waypoints = path:GetWaypoints()
local blocked = false
local blockedConnection = path.Blocked:Connect(function(blockedIndex)
blocked = true -- an obstacle appeared on a waypoint ahead; caller recomputes
print("path blocked at waypoint", blockedIndex)
end)
for index = 2, #waypoints do
local waypoint = waypoints[index]
if waypoint.Action == Enum.PathWaypointAction.Jump then
humanoid.Jump = true
end
humanoid:MoveTo(waypoint.Position)
local reached = humanoid.MoveToFinished:Wait() -- false after 8 s timeout
if not reached or blocked or humanoid.Health <= 0 then
blockedConnection:Disconnect()
return false
end
end
blockedConnection:Disconnect()
return true
end
return walkToCreatePath+ComputeAsyncis the current API (FindPathAsyncis superseded). ReusePathobjects per agent type;ComputeAsyncyields — never call it every frame for every NPC.- Chasing moving targets: recompute when the target moved more than a few studs since the last
compute or every ~0.5–1 s, and skip pathfinding entirely when there's a clear line of sight
(
workspace:Raycastfrom NPC to target) — justMoveTothe target. PathfindingModifier(child of a part/region,Labelmatching aCostskey,PassThroughfor doors) andPathfindingLink(between two attachments, e.g. ladders, teleporters, boats; the waypoint hasLabelandAction = Custom) shape paths.- Limits: start→goal straight-line distance ≤ 3,000 studs, 20,000-node budget; far goals need intermediate targets. Visualize the navmesh with Studio's Navigation Mesh visualization option.
- On clients, pathfinding sees only streamed-in geometry: compute paths on the server.
Behavior: state machine
--!strict
type State = "Idle" | "Chase" | "Attack" | "Return"
type Npc = {
model: Model,
humanoid: Humanoid,
home: Vector3,
state: State,
target: Model?,
lastAttack: number,
}
local AGGRO_RANGE = 40
local ATTACK_RANGE = 5
local LEASH_RANGE = 80
local ATTACK_COOLDOWN = 1.2
local losParams = RaycastParams.new()
losParams.FilterType = Enum.RaycastFilterType.Exclude
local function hasLineOfSight(from: Vector3, target: BasePart, ignore: { Instance }): boolean
losParams.FilterDescendantsInstances = ignore
local result = workspace:Raycast(from, target.Position - from, losParams)
return result == nil or result.Instance:IsDescendantOf(target.Parent :: Instance)
end
local function think(npc: Npc, findTarget: (Vector3, number) -> Model?)
local root = npc.humanoid.RootPart
if not root or npc.humanoid.Health <= 0 then
return
end
local targetRoot = npc.target and npc.target:FindFirstChild("HumanoidRootPart") :: BasePart?
local distance = if targetRoot then (targetRoot.Position - root.Position).Magnitude else math.huge
if npc.state == "Idle" then
npc.target = findTarget(root.Position, AGGRO_RANGE)
if npc.target then
npc.state = "Chase"
end
elseif npc.state == "Chase" then
if not targetRoot or (root.Position - npc.home).Magnitude > LEASH_RANGE then
npc.state = "Return"
elseif distance <= ATTACK_RANGE then
npc.state = "Attack"
elseif hasLineOfSight(root.Position, targetRoot, { npc.model }) then
npc.humanoid:MoveTo(targetRoot.Position)
end -- else: request a path (throttled) via walkTo in a separate thread
elseif npc.state == "Attack" then
if not targetRoot or distance > ATTACK_RANGE * 1.5 then
npc.state = "Chase"
elseif os.clock() - npc.lastAttack >= ATTACK_COOLDOWN then
npc.lastAttack = os.clock()
local targetHumanoid = npc.target and npc.target:FindFirstChildOfClass("Humanoid")
if targetHumanoid then
targetHumanoid:TakeDamage(10)
end
end
elseif npc.state == "Return" then
npc.target = nil
npc.humanoid:MoveTo(npc.home)
if (root.Position - npc.home).Magnitude < 4 then
npc.state = "Idle"
end
end
end
return thinkFor complex AI (many behaviors, priorities), use a behavior tree or utility AI; keep the same "think at a fixed rate, act through small primitives" structure. See references/scaling-npcs.md for spawning, pooling, and performance with hundreds of NPCs.
Perception tips
- Find targets with
workspace:GetPartBoundsInRadiusagainst a collision group/filter containing only characters, or iteratePlayers:GetPlayers()(cheaper for small counts) and compare squared distances. - Add reaction delays and field-of-view checks (
root.CFrame.LookVector:Dot(direction.Unit) > 0.5) so NPCs feel fair. - Stagger thinking:
npcIndex % 5 == frame % 5spreads work across frames.
Related skills
roblox-characters-animation (humanoids, animation), roblox-physics (ownership, raycasts),
roblox-performance (profiling, parallel Luau), roblox-game-systems (waves, combat, drops).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

