关于 roblox-npc-ai
面向 Roblox 开发者的 NPC AI 实现指南,介绍服务端权威逻辑、寻路、状态机和目标感知。重点是以固定频率集中处理 NPC,避免逐个脚本持续循环造成负担。
主要功能
- 01在服务端运行 NPC 权威逻辑
- 02集中循环并错峰处理 NPC
- 03使用 PathfindingService 计算路径
- 04通过状态机组织 NPC 行为
- 05按距离或视野检测目标
- 06配置寻路修饰器与连接点
适用场景
- 01实现追逐玩家的 NPC
- 02设计巡逻、攻击和返回等行为
- 03为大量 NPC 降低寻路与计算负担
- 04配置门、梯子等特殊路径
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ el4cteo/ roblox-skills --skill roblox-npc-ai安装后在 Claude Code 中输入/roblox-npc-ai确认已加载。
附带文件references/scaling-npcs.md
详细介绍
NPC 与 AI
架构
- NPC 逻辑在服务端运行(权威目标、伤害、掉落)。视觉附加效果(待机动画、名牌、特效)可以在客户端运行。
- 一个 NPC 服务通过单个循环驱动所有 NPC(例如 5–10 Hz 的“思考”频率,错开执行),而不是每个 NPC 一个脚本 +
while true。给 NPC 模型打标签(CollectionService),并将每个 NPC 的状态保存在表中。 - 服务端拥有物理所有权:生成后执行
npc.PrimaryPart:SetNetworkOwner(nil),这样当所有权在附近玩家之间转移时 NPC 不会卡顿(利用漏洞者也无法推动它们)。 - 对于需要行走/跳跃/攀爬的 NPC,使用
Humanoid;对于简单移动体(炮塔、漂浮敌人、群体),使用AnimationController+Animator,并通过约束或 CFrame 移动。
寻路
--!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 walkTo
CreatePath+ComputeAsync是当前 API(FindPathAsync已被取代)。按 Agent 类型复用Path对象;ComputeAsync会让出执行——绝不要每帧为每个 NPC 调用它。- 追逐移动目标:当目标自上次计算以来移动超过几 studs 或每隔约 0.5–1 s 重新计算;当有清晰视线时完全跳过寻路(从 NPC 到目标执行
workspace:Raycast)——只需MoveTo目标。 PathfindingModifier(作为 part/region 的子对象,Label匹配Costs键,PassThrough用于门)和PathfindingLink(位于两个 attachment 之间,例如梯子、传送器、船;路点带有Label和Action = Custom)会塑造路径。- 限制:起点→终点直线距离 ≤ 3,000 studs,节点预算为 20,000;较远的目标需要中间目标。使用 Studio 的 Navigation Mesh 可视化选项来可视化导航网格。
- 在客户端,寻路只能看到已流入的几何体:请在服务端计算路径。
行为:状态机
--!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 think
对于复杂 AI(多种行为、优先级),请使用行为树或效用 AI;保持相同的“以固定频率思考,通过小型原语行动”的结构。有关数百个 NPC 的生成、池化和性能,请参阅 references/scaling-npcs.md。
感知提示
- 通过
workspace:GetPartBoundsInRadius针对仅包含角色的碰撞组/过滤器查找目标,或遍历Players:GetPlayers()(数量较少时更便宜)并比较平方距离。 - 添加反应延迟和视野检查(
root.CFrame.LookVector:Dot(direction.Unit) > 0.5),让 NPC 感觉公平。 - 错开思考:
npcIndex % 5 == frame % 5将工作分散到各帧。
相关技能
roblox-characters-animation(humanoid、动画)、roblox-physics(所有权、射线检测)、roblox-performance(性能分析、并行 Luau)、roblox-game-systems(波次、战斗、掉落)。
评论
支持 Markdown