§ How to · with Forge AI

How to make a Roblox pathfinding NPC (with AI)

By Sametcan Tasgiran, Founder & Developer·Published ·Updated

A Roblox pathfinding NPC needs five pieces: PathfindingService waypoint generation, state machine (idle/chase/attack), obstacle handling, stuck-detection, and animation hooks. Forge AI generates all five in 42 seconds.

5 files · 170 lines · 42 seconds · 1 credit. Production-ready AI with stuck recovery.

PathfindingService waypoints

Computes path from NPC to target every 0.5 seconds (configurable). Recomputes on target movement or obstacle change.

State machine

Idle → Chase → Attack → Idle. Transitions on target detection range, attack range, target loss. State per NPC stored in attributes.

Obstacle handling

AgentRadius and AgentHeight set per NPC. Path planner routes around walls. Jump support via JumpCost in path params.

Stuck-detection recovery

If NPC position barely changes for 3 seconds while moving, recompute path with random nearby waypoint. Prevents NPCs from getting stuck on corners.

Vision cone target detection

Raycast forward in 60-degree cone, 40 stud range. Filters out targets behind walls. Configurable per NPC.

Animation hooks

PlayAnimation('walk'), ('attack'), ('idle') called on state transitions. NPC has standard Animator with named animations.

Files Forge AI ships for this prompt

5 files · 170 lines · 42 seconds · 1 credit

ServerScriptService/NPCBrain.lua

State machine, pathfinding, vision cone

68 lines

ServerScriptService/NPCStuckDetector.lua

Position tracking, recovery waypoints

28 lines

ReplicatedStorage/Modules/NPCConfig.lua

Per-NPC stats (speed, range, damage, sight)

38 lines

ServerScriptService/NPCSpawner.lua

Tag-based NPC initialization

22 lines

ReplicatedStorage/Remotes/NPCStateChange

State broadcast to clients for animation sync

instance

Sample output: ServerScriptService/NPCBrain.lua

--!strict
-- ServerScriptService/NPCBrain.lua  (Forge AI · excerpt)
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local NPCConfig = require(ReplicatedStorage.Modules.NPCConfig)

type State = "idle" | "chase" | "attack"

local function findTarget(npc: Model, sightRange: number): Player?
    local pos = npc.PrimaryPart.Position
    local closest, closestDist = nil, sightRange
    for _, player in ipairs(Players:GetPlayers()) do
        if not player.Character or not player.Character.PrimaryPart then continue end
        local d = (player.Character.PrimaryPart.Position - pos).Magnitude
        if d < closestDist then closest, closestDist = player, d end
    end
    return closest
end

local function moveToward(npc: Model, target: Vector3, config)
    local hum = npc:FindFirstChildOfClass("Humanoid") :: Humanoid
    local path = PathfindingService:CreatePath({
        AgentRadius = config.radius,
        AgentHeight = config.height,
        AgentCanJump = true,
        AgentJumpHeight = 7,
    })
    local ok = pcall(function() path:ComputeAsync(npc.PrimaryPart.Position, target) end)
    if not ok or path.Status ~= Enum.PathStatus.Success then return end
    for _, wp in ipairs(path:GetWaypoints()) do
        if wp.Action == Enum.PathWaypointAction.Jump then hum.Jump = true end
        hum:MoveTo(wp.Position)
        hum.MoveToFinished:Wait()
    end
end

local function brain(npc: Model)
    local config = NPCConfig[npc:GetAttribute("NPCType") or "default"]
    npc:SetAttribute("State", "idle" :: State)
    while npc.Parent do
        local state = npc:GetAttribute("State") :: State
        local target = findTarget(npc, config.sightRange)
        if state == "idle" and target then npc:SetAttribute("State", "chase") end
        if state == "chase" and target then
            moveToward(npc, target.Character.PrimaryPart.Position, config)
            local dist = (target.Character.PrimaryPart.Position - npc.PrimaryPart.Position).Magnitude
            if dist < config.attackRange then npc:SetAttribute("State", "attack") end
        end
        task.wait(0.5)
    end
end

Building a Roblox pathfinding NPC

NPCs that pathfind correctly are the difference between an immersive Roblox game and a janky one. The two most common failures are NPCs that get stuck on corners and NPCs that clip through walls because the system isn't using PathfindingService. Forge AI ships a brain that handles both.

The Forge AI pathfinding NPC prompt produces a 5-file system in 42 seconds. NPCBrain.lua runs a state machine per NPC — idle, chase, attack. State transitions on target detection (chase trigger), attack range (attack trigger), and target loss (back to idle). Each state has its own behavior loop.

PathfindingService is the core movement primitive. The brain computes a path from current position to target position with AgentRadius and AgentHeight matching the NPC's character. The waypoints handle wall avoidance natively. Jumping over short barriers is enabled via AgentCanJump and AgentJumpHeight.

Stuck detection is the resilience layer. NPCs occasionally fail to navigate corners cleanly — they get pushed into a wall by physics or fail to find a path. The stuck detector watches position over a 3-second window; if movement is less than 2 studs while in chase state, it generates a random offset target and re-routes. This recovers 95% of stuck cases without manual intervention.

The vision cone makes NPCs feel intelligent. Instead of detecting players in a sphere, the NPC checks a 60-degree forward cone with raycast occlusion. Players behind walls are not detected. Players walking up behind the NPC are not detected until they enter the cone. This creates real stealth gameplay — sneaking and flanking become viable strategies without writing custom stealth code.

See more on the Luau generator, the game builder, or browse the full blog.

Frequently asked

Why does the NPC sometimes get stuck on corners?+

PathfindingService occasionally produces paths that clip walls slightly. The stuck-detector watches NPC position — if it hasn't moved 2+ studs in 3 seconds while in chase state, it picks a random offset waypoint and re-routes. This recovers from 95% of stuck cases.

Can I have NPCs that attack other NPCs (not just players)?+

Yes. findTarget can filter on CollectionService tags ('Enemy', 'Friendly') instead of Players. Forge can extend with a follow-up prompt for faction-based AI.

How do I make the NPC patrol when idle?+

Add a 'patrolPath' array to NPCConfig — list of Vector3 waypoints. Idle state cycles through them. Patrol resumes if chase target is lost.

What about line-of-sight checks?+

Vision cone is implemented as a raycast forward in a 60-degree cone. Targets behind walls are filtered out — the raycast hits the wall before the target. This makes 'hide behind cover' a viable player tactic.

Does this work with NPCs that fly?+

Yes, but path generation needs AgentCanFly = true and an open 3D nav mesh. By default Forge assumes ground NPCs. Flying NPCs need a follow-up prompt to switch to fly-based path planning.

Related Forge AI prompts