寻路 是移动角色沿着逻辑路径到达目的地的过程,避免障碍和 (可选) 危险材料或定义区域。
导航视图
为了在路径找寻布局和调试中提供帮助,Studio 可以渲染一个导航网格和 调整 标签。 要启用它们,请从 3D 视窗的右上角的 视图选项 小组中的 导航网格 和 2>调整2> 标签切换。
启用了 导航网格 ,彩色区域显示角色可能走路或游泳的地方,而非颜色区域是否被封锁。小箭头指示区域,角色可以通过跳跃尝试达到,假设您设置了 AgentCanJump 为true,当1> 创建路径1> 时。
使用 路径修改器 时,文本标签会指示具体材料和区域,当使用 路径修改器 时考虑到。
已知的限制
路径找寻功能具有特定限制,以确保有效处理和最佳性能。
垂直放置限制
路径寻找计算仅考虑垂直边界内的部分:
- 低限制 — 零件的底部 Y 坐标小于 -65,536 格是无视的。
- 上限 — 超出 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 和它们的价格为 traversal。 有助于使代理优先使用某些材料/区域 over others。 请参阅modifiers 获取详细信息。 | 表 | nil |
本地脚本
local PathfindingService = game:GetService("PathfindingService")local path = PathfindingService:CreatePath({AgentRadius = 3,AgentHeight = 6,AgentCanJump = false,Costs = {Water = 20}})
注意,当您设置 TrussParts 为 AgentCanClimb 时,当您创建路径时,并且没有什么阻止路径从桁架上攀爬,当前可以攀爬的路径有 true
本地脚本 - 桁架梯子路径
local PathfindingService = game:GetService("PathfindingService")local path = PathfindingService:CreatePath({AgentCanClimb = true,Costs = {Climb = 2 -- 爬行道的费用;默认为 1}})
沿路径移动
本节使用以下路径寻找脚本为玩家的角色。在阅读时测试:
- 将代码复制到 LocalScript 内的 StarterCharacterScripts 。
- 编辑线 11 到一个 Vector3 目的地,玩家角色可以达到。
- 阅读以下部分了解路径计算和角色移动。
本地脚本 - 角色路径找寻
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
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() 之后,它必须通过调用 Class.Path:ComputeAsync() 与一个 8> Datatype.Vector3 为开始点和目的地。
本地脚本 - 角色路径找寻
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
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 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 ) 和一个 行动 ( 2> Class.Humanoid:MoveTo2> ) 组
本地脚本 - 角色路径找寻
local PathfindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
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 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() 返回最短路径,从开始点和目的地之间,以除避免跳跃。 这在某些情况下看起来不自然,例实例路径通过水而不是通过附近的桥梁,因为水路通过的路径几何上更短。
要进一步优化路径寻找,您可以实现 路径寻找修改 来计算更智能的路径通过各种材料、定义区域和1>障碍1>。
设置材料成本
使用 Terrain 和 BasePart 材料时,您可以包含一个 Costs 表在 1>Class.PathfindingService:CreatePath()|CreatePath() 内,以使某些材料比其他材料更可行走。所有材料的
在 Costs 表中,键名应该是代表 Enum.Material 名称的字符串名,例如 Water 对于 1> Enum.Material.Water1> .
本地脚本 - 角色路径找寻
local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local path = PathfindingService:CreatePath({Costs = {Water = 20,Mud = 5,Neon = math.huge}})
与区域工作
在某些情况下,材质优先级 不足。例如,您可能希望角色避免 定义区域 ,无论是否使用脚下的材料。这可以通过添加一个PathfindingModifier 对象来实现。
创建一个 Anchored 部分在危险区域周围,并将其 CanCollide 属性设置为 否 。
将 PathfindingModifier 实例插入零件,找到其 Label 属性,并为其命名为 危险区域 。
包含一个 Costs 表,其中包含一个与钥匙匹配的数字值。一个调整因素可以被定义为不可交叉,通过将其值设置为 CreatePath() 。
本地脚本 - 角色路径找寻local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")local path = PathfindingService:CreatePath({Costs = {DangerZone = math.huge}})
忽略障碍
在某些情况下,通过“作为 if 不存在”的方式通过固体障碍物是有用的。这允许您通过特定物理方块计算路径,而不是通过计算失败。
在对象周围创建一个 Anchored 零件,设置其 CanCollide 属性为 否 。
在零件上插入一个 PathfindingModifier 实例,并启用其 PassThrough 属性。
现在,当路径从僵尸 NPC 到玩家角色时,路径将延伸到门之外,并且您可以提示僵尸穿过它。即使僵尸无法打开门,它也会反应为“听”到门后的角色。
寻路链接
有时候,您需要找到一个通过空间不能通过正常方式穿越的路径,例如穿越峡谷。为此,您可以使用 PathfindingLink 对象。
使用上面的岛屿示例,您可以让代理使用船而不是走过所有的桥。
使用此示例创建一个 PathfindingLink:
为了协助于视图化和调试,从 3D 视窗的右上角的 视图选项 小组中切换 寻路链接 。
在船上的座位上创建两个 Attachments ,一个在船上的落地点附近。
在工作区中创建一个 PathfindingLink 对象,然后分别为其添加 附件0 和 附件1 属性。
将有意义的名称,例如 使用船只 分配到其 Label 属性。 此名称在路径寻找脚本中作为标志在路径寻找脚本中使用,当代理到达起始链接时触发自定义操作。
在 Costs 中包含一个包含 CreatePath() 键和一个与 Water 属性名称匹配的自定义键。 将自定义键设置为低于 2>Water2> 。
本地脚本 - 角色路径找寻local PathfindingService = game:GetService("PathfindingService")local Players = game:GetService("Players")local RunService = game:GetService("RunService")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 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 的最佳实践:
流媒体可以阻塞或解锁给定路径,因为角色沿着它移动。例如,当角色通过森林时,树可能会从某个地方流入前方并阻塞道路。为了使路径找到顺畅工作,它非常建议您使用处理阻塞道路技术,并在必要时重新计算道路。
在寻路方法中,使用现有对象的坐标进行<a href=\"#计算\">计算</a>,例如将路径目的地设置为现有 <a href=\"#TreasureChest|TreasureChest_in_the_world\">Treasure
为了解决这个问题,请考虑将目的地设置为 BasePart 在持久模型中的位置。持久模型在玩家加入后很快加载,它们永远不会流出,因此客户端脚本可以连接到 Class.Workspace.PersistentLoaded|PersistentLoaded 事件,从而安全地访问事件发生后的路径创建。