路径探索 是将角色移动到逻辑路径上以达到目的地的过程,避免障碍物和(可选)危险物质或定义区域。
导航视觉化
为了帮助路径拓扑布置和调试,Studio 可以渲染导航网格和 调整器 标签。要启用它们,请在 3D 视图右上角的 视觉选项 选项卡中切换 导航网格 和 路径找到修改器 ,以启用它们。

启用了 导航网格 后,彩色区域显示角色可能走路或游泳的地方,而非彩色区域被阻止。小箭头表示角色可以通过跳跃尝试到达的区域,假设你在创建路径时将 设置为 ,当 创建路径时。

启用了 路径匹配修改器 ,文本标签表示在使用路径匹配修改器时考虑的特定材料和区域。

已知限制
路径探索功能具有特定限制,以确保高效处理和最佳性能。
垂直放置限制
路径找到计算只考虑特定垂直边界内的零件:
- 下限 — 部件的底部 Y 坐标小于 -65,536 格,将被忽略。
- 上限 — 拥有顶部 Y 坐标超过 65,536 个螺柱的零件被忽略。
- 垂直距离 — 从最低部分底部 Y 坐标到最高部分顶部 Y 坐标的垂直距离不得超过 65,536 个螺柱;否则,路径拓扑系统在路径计算时将忽略这些部分。
搜索距离限制
从起始点到终点的路径拓展直接视觉距离不得超过 3,000 个单位。超过此距离会导致NoPath状态。
创建路径
路径探索通过 PathfindingService 和其 CreatePath() 函数启动。
本地脚本
local PathfindingService = game:GetService("PathfindingService")local path = PathfindingService:CreatePath()
CreatePath() 接受一个可选的参数表,该参数表可调整角色(代理)沿路移动的方式。
关键 | 描述 | 类型 | 默认 |
---|---|---|---|
AgentRadius | 代理半径,以点为单位。有用于确定最小与障碍物分离的距离。 | 整数 | 2 |
AgentHeight | 代理高度,以学分为单位。小于此值的空间,例如楼梯下的空间,将被标记为不可穿越。 | 整数 | 5 |
AgentCanJump | 决定是否允许在路径探索期间跳跃。 | boolean 类型 | true |
AgentCanClimb | 决定是否允许在路径探索期间攀登TrussParts。 | boolean 类型 | false |
WaypointSpacing | 路径中的中间路径点之间的间隔。如果设置为 math.huge,将不存在中间路径点。 | 数字 | 4 |
Costs | 材料表或定义的PathfindingModifiers和其通过成本。有助于使代理更喜欢某些材料/区域而不是其他材料/区域。见 调整因素 获取详细信息。 | 表 | nil |
本地脚本
local PathfindingService = game:GetService("PathfindingService")local path = PathfindingService:CreatePath({AgentRadius = 3,AgentHeight = 6,AgentCanJump = false,Costs = {Water = 20}})
请注意,在路径拓扑期间,代理可以攀爬 ,假设你将 设置为 ,当创建路径时,没有任何东西阻止代理从桁架攀爬路径。攀爬路径具有 攀爬 标签,攀爬路径的 成本 默认为 1 。

本地脚本 - 桁架攀登路径
local PathfindingService = game:GetService("PathfindingService")local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {Climb = 2 -- 攀登路径的成本;默认值为 1}})
沿路移动
本节使用以下路径抓取脚本为玩家角色。要在阅读时测试:
- 将代码复制到 LocalScript 内的 StarterCharacterScripts 中。
- 将 变量设置为你的 3D 世界中的一个目的地,玩家角色可以到达。
- 通过以下部分继续学习路径计算和角色移动。
本地脚本 - 角色路径匹配
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- 计算路径
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- 获取路径路径点
waypoints = path:GetWaypoints()
-- 检测路径是否被阻塞
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- 检查障碍物是否在路径下方
if blockedWaypointIndex >= nextWaypointIndex then
-- 在路径重计算后停止检测路径阻塞
blockedConnection:Disconnect()
-- 调用函数重新计算新路径
followPath(destination)
end
end)
-- 检测当前移动到下一个路径点完成时
if not reachedConnection then
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- 增加路径索引并移至下一个路径点
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
end
-- 初始移动到第二个路径点(第一个路径点是路径开始;跳过它)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end
followPath(TEST_DESTINATION)
计算路径
在您创建有效路径 CreatePath() 之后,必须通过调用 **** 来计算 Path:ComputeAsync() 对起始点和目的地的 Vector3 。
本地脚本 - 角色路径匹配
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- 计算路径
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
end

获取路径点
一旦 Path 计算完成,它将包含一系列 路径节点 ,可以追踪从开始到结束的路径。这些点可以用 Path:GetWaypoints() 函数收集。
本地脚本 - 角色路径匹配
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- 计算路径
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- 获取路径路径点
waypoints = path:GetWaypoints()
end
end

路径移动
每个路径点包括一个 位置 ( Vector3 ) 和一个 行动 ( PathWaypointAction )。要移动包含 Humanoid 的角色,例如典型 Roblox 角色,最简单的方法是从路径点到路径点调用 Humanoid:MoveTo() ,使用 MoveToFinished 事件检测角色何时到达每个路径点。
本地脚本 - 角色路径匹配
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- 计算路径
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- 获取路径路径点
waypoints = path:GetWaypoints()
-- 检测路径是否被阻塞
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- 检查障碍物是否在路径下方
if blockedWaypointIndex >= nextWaypointIndex then
-- 在路径重计算后停止检测路径阻塞
blockedConnection:Disconnect()
-- 调用函数重新计算新路径
followPath(destination)
end
end)
-- 检测当前移动到下一个路径点完成时
if not reachedConnection then
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- 增加路径索引并移至下一个路径点
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
end
-- 初始移动到第二个路径点(第一个路径点是路径开始;跳过它)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end
处理被阻止的路径
许多 Roblox 世界是动态的;零件可能会移动或掉落,地板可能会坍塌。这可以阻止计算出的路径并防止角色达到目的地。为了处理这一问题,您可以连接 Path.Blocked 事件并重新计算阻止它的路径。
本地脚本 - 角色路径匹配
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local path = PathfindingService:CreatePath()
local player = Players.LocalPlayer
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local TEST_DESTINATION = Vector3.new(100, 0, 100)
local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local function followPath(destination)
-- 计算路径
local success, errorMessage = pcall(function()
path:ComputeAsync(character.PrimaryPart.Position, destination)
end)
if success and path.Status == Enum.PathStatus.Success then
-- 获取路径路径点
waypoints = path:GetWaypoints()
-- 检测路径是否被阻塞
blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
-- 检查障碍物是否在路径下方
if blockedWaypointIndex >= nextWaypointIndex then
-- 在路径重计算后停止检测路径阻塞
blockedConnection:Disconnect()
-- 调用函数重新计算新路径
followPath(destination)
end
end)
end
end
路径找到修改器
默认情况下,Path:ComputeAsync() 返回起始点和目的地之间的 最短路径 ,除了它试图避免跳跃。在某些情况下,这看起来不自然——例实例,一条路可能会穿过水而不是穿过附近的桥,仅因为穿过水的路径更短。

为了进一步优化路径探索,您可以通过实现 路径修改器 来计算更聪明的路径通过各种材料、环绕定义的区域或通过障碍物来。
设置材料成本
当与 Terrain 和 BasePart 材料工作时,您可以在 Costs 内包含一张 CreatePath() 表,以使某些材料比其他材料更容易穿越。所有材料的默认成本为 1 ,任何材料都可以通过设置其值为 math.huge 来定义为不可穿越。
在 Costs 表中的键应为代表 Enum.Material 名称的字符串名称,例如 Water 为 Enum.Material.Water .
本地脚本 - 角色路径匹配
local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local Workspace = game:GetService("Workspace")local path = PathfindingService:CreatePath({Costs = {Water = 20,Mud = 5,Neon = math.huge}})
与区域合作
在某些情况下,材料偏好不足以满足需求。例如,你可能希望角色避免 定义的区域 ,无论是否有材料在脚下。这可以通过将 PathfindingModifier 对象添加到零件来实现。
在危险区域周围创建一个 Anchored 部分,将其 CanCollide 属性设置为 false 。
将 PathfindingModifier 实例插入零件,找到其 Label 属性,然后分配一个有意义的名称,例如 危险区 。
包含一个含有匹配键和相关数值的 Costs 表在 CreatePath() 内,其中包含匹配的键。修改器可以通过将其值设置为 math.huge 来定义为不可遍历。
本地脚本 - 角色路径匹配local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local Workspace = game:GetService("Workspace")local path = PathfindingService:CreatePath({Costs = {DangerZone = math.huge}})
忽略障碍物
在某些情况下,通过假装没有障碍物来寻找路径是有用的。这可以让你计算特定物理障碍的路径,而不是计算完全失败。
在对象周围创建一个 Anchored 部分,并将其 CanCollide 属性设置为 false 。
将 PathfindingModifier 实例插入零件并启用其 PassThrough 属性。
现在,当从僵尸 NPC 到玩家角色的路径计算时,路径会超出门,你可以向僵尸提示通过它。即使僵尸无法打开门,它也像是“听到”了门后面的角色。
寻路链接
有时需要找到一个通过无法正常穿越的空间的路径,例如穿过峡谷,然后执行自定义行动来到达下一个路径点。这可以通过 PathfindingLink 对象实现。
使用上面的岛屿示例,您可以让代理使用船而不是穿过所有桥梁来行走。

要使用此例子创建一个 PathfindingLink :
要帮助进行可视化和调试,请在 3D 视图右上角的 视觉选项 选项卡中切换 路径拓扑链接 ,从而显示路径。
创建两个 Attachments , 一个在船的座位上, 一个靠近船的起落点。
将具有意义的名称 like UseBoat 分配到其 Label 属性。该名称在路径抓取脚本中用作旗帜,当代理达到起始链接点时触发自定义行动。
包含一个 表,内含 both a 键和一个匹配 属性名称的自定义键。为自定义键分配低于 Water 的值。
本地脚本 - 角色路径匹配local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local Workspace = game:GetService("Workspace")local path = PathfindingService:CreatePath({Costs = {Water = 20,UseBoat = 1}})在达到路径节点时触发的事件中,添加自定义检查到 Label 修改器名称,并采取与 Humanoid:MoveTo() 不同的行动——在这种情况下,调用函数将代理安装在船上,将船移到水上,然后在到达目的地岛时继续代理的路径。
本地脚本 - 角色路径匹配local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local Workspace = game:GetService("Workspace")local path = PathfindingService:CreatePath({Costs = {Water = 20,UseBoat = 1}})local player = Players.LocalPlayerlocal character = player.Characterlocal humanoid = character:WaitForChild("Humanoid")local TEST_DESTINATION = Vector3.new(228.9, 17.8, 292.5)local waypointslocal nextWaypointIndexlocal reachedConnectionlocal blockedConnectionlocal function followPath(destination)-- 计算路径local success, errorMessage = pcall(function()path:ComputeAsync(character.PrimaryPart.Position, destination)end)if success and path.Status == Enum.PathStatus.Success then-- 获取路径路径点waypoints = path:GetWaypoints()-- 检测路径是否被阻塞blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)-- 检查障碍物是否在路径下方if blockedWaypointIndex >= nextWaypointIndex then-- 在路径重计算后停止检测路径阻塞blockedConnection:Disconnect()-- 调用函数重新计算新路径followPath(destination)endend)-- 检测当前移动到下一个路径点完成时if not reachedConnection thenreachedConnection = humanoid.MoveToFinished:Connect(function(reached)if reached and nextWaypointIndex < #waypoints then-- 增加路径索引并移至下一个路径点nextWaypointIndex += 1-- 如果路径标签是“使用船”,请使用船;否则移动到下一个路径点if waypoints[nextWaypointIndex].Label == "UseBoat" thenuseBoat()elsehumanoid:MoveTo(waypoints[nextWaypointIndex].Position)endelsereachedConnection:Disconnect()blockedConnection:Disconnect()endend)end-- 初始移动到第二个路径点(第一个路径点是路径开始;跳过它)nextWaypointIndex = 2humanoid:MoveTo(waypoints[nextWaypointIndex].Position)elsewarn("Path not computed!", errorMessage)endendfunction useBoat()local boat = Workspace.BoatModelhumanoid.Seated:Connect(function()-- 如果代理坐下,开始船只移动if humanoid.Sit thentask.wait(1)boat.CylindricalConstraint.Velocity = 5end-- 检测岛与约束位置之间的关系local boatPositionConnectionboatPositionConnection = RunService.PostSimulation:Connect(function()-- 在岛屿旁停止船只if boat.CylindricalConstraint.CurrentPosition >= 94 thenboatPositionConnection:Disconnect()boat.CylindricalConstraint.Velocity = 0task.wait(1)-- 卸下代理并继续前往目的地humanoid.Sit = falsehumanoid:MoveTo(waypoints[nextWaypointIndex].Position)endend)end)endfollowPath(TEST_DESTINATION)
传输兼容性
体验中的实例流是一个强大的功能,可以动态加载和卸载 3D 内容,随着玩家角色在世界上移动。当他们探索 3D 空间时,新的子集流向他们的设备,现有的一些子集可能会流出。
考虑以下最佳实践,用于在启用流式体验时使用 PathfindingService:
传播可以阻止或解锁给定的路径,当角色沿着它移动时。例如,当角色通过森林时,树可能会在他们前面的某个地方流入,并阻碍路径。为了使路径抓取与流媒体无缝工作,强烈建议您使用 处理阻塞路径 技术并在必要时重新计算路径。
在路径寻找中的一个常见方法是使用现有对象的坐标进行 计算,例如将路径目的地设置为世界上现有 宝箱模型 的位置。这种方法与服务器端 Scripts 完全兼容,因为服务器始终拥有世界的全景,但 LocalScripts 和 ModuleScripts 运行在客户端的可能会失败,如果它们尝试计算不在输入中流式传输的对象的路径。
为了解决这个问题,请考虑将目的地设置为永久模型中的 位置。持续模型很快就会加载到玩家加入后,它们永远不会流出,因此客户端脚本可以连接到 PersistentLoaded 事件并安全地访问模型,以在事件发生后创建路径点。