キャラクターパスファインド

*このコンテンツは、ベータ版のAI(人工知能)を使用して翻訳されており、エラーが含まれている可能性があります。このページを英語で表示するには、 こちら をクリックしてください。

パス探索 は、論理的なパスを移動して目的地に到達するプロセスで、障害物を避け、また(オプションで)危険な材料や定義された領域を避ける。

ナビゲーション視覚化

パスファインディングレイアウトとデバッグを助けるため、Studio はナビゲーションメッシュと 修正者 ラベルをレンダリングできます。有効にするには、3Dビューポートの右上隅の ビジュアライゼーションオプション ウィジェットから ナビゲーションメッシュパス探索修正子 を切り替え、有効にします。

A close up view of the 3D viewport with the Visualization Options button indicated in the upper-right corner.

ナビゲーションメッシュ を有効にすると、カラーのある領域ではキャラクターが歩くか水泳可能性が示され、カラーのない領域はブロックされます。小さな矢印は、キャラクターがジャンプして到達しようとする領域を示し、AgentCanJumptrue に設定したと仮定して、パスを作成するとき に。

Navigation mesh showing in Studio

パス探索修正子を有効にした場合、 テキストラベルは、 パス探索修正子を使用するときに考慮される特定の材料と領域を示します。

Navigation labels showing on navigation mesh

既知の制限

パスファインド機能は、効履行的な処理と最適なパフォーマンスを保証するための特定の制限を持っています。

縦向き配置制限

パスファインド計算は、特定の垂直境界内のパーツのみを考慮します:

  • 下限 — 下限 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パス探索中にジャンプが許可されているかどうかを決定します。ブールン値true
AgentCanClimbパス探索中に TrussParts を登ることが許可されているかどうかを決定します。ブールン値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 です。

Path going up a climbable TrussPart ladder
ローカルスクリプト - トラス登りパス

local PathfindingService = game:GetService("PathfindingService")
local path = PathfindingService:CreatePath({
AgentCanClimb = true,
Costs = {
Climb = 2 -- 登り道のコスト; デフォルトは 1
}
})

パスを移動する

このセクションでは、プレイヤーのキャラクターのための次のパス探索スクリプトを使用します。読み込み中にテストするには:

  1. コードを LocalScript 内の StarterCharacterScripts にコピーします。
  2. TEST_DESTINATION 変数を、プレイヤーキャラクターが到達できる3DワールドのVector3 目的地に設定します。
  3. 次のセクションを進んで、パス計算とキャラクター移動について学びましょう。
ローカルスクリプト - キャラクターパス検索

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
-- 最初は2番目のウェイポイントに移動(最初のウェイポイントはパスの開始; スキップする)
nextWaypointIndex = 2
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
warn("Path not computed!", errorMessage)
end
end
followPath(TEST_DESTINATION)

パスを計算する

After you've created a valid path with CreatePath() , it must be 計算される by calling Path:ComputeAsync() with a 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 start/end marked on series of islands and bridges

ウェイポイントを取得

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
Waypoints indicated across computed path
計算されたパス全体に示されたウェイポイント

パス移動

各ウェイポイントは、位置()とアクション()の両方から構成されています。典型的な Roblox キャラクターのように、キャラクターに Humanoid が含まれている場合、最も簡単な方法は、waypoint から waypoint へ Humanoid:MoveTo() を呼び出し、キャラクターが各waypointに到達したときに検出する 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
-- 最初は2番目のウェイポイントに移動(最初のウェイポイントはパスの開始; スキップする)
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() は、スタートポイントと目的地の間の 最短のパス を返し、ジャンプを避けようとするのを除きます。これは、いくつかの状況で自然ではないように見えます - たとえば、水を通る道が単に近くの橋よりも短いため、水を通る道を幾何学的に短縮することができます。

Two paths indicated with the shorter path not necessarily more logical

さらにパス探索を最適化するには、 パス探索修正子 を実装して、さまざまな材料、定義された領域、または障害物を通じて賢明なパスを計算できます。

材料コストを設定

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 オブジェクトを追加することで実現できます。

  1. 危険な領域のまわりに Anchored パーツを作成し、その CanCollide プロパティを false に設定します。

    Anchored part defining a region to apply a pathfinding modifier to
  2. パーツに PathfindingModifier インスタンスを挿入し、その Label プロパティを見つけ、意味のある名前 like DangerZone を割り当てます。

    PathfindingModifier instance with Label property set to DangerZone
  3. 一致するキーと関連する数値を含む 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
    }
    })

障害を無視する

いくつかの場合、堅い障害物を通り抜けるのは、存在しないかのように有用です。これにより、特定の物理ブロッカーを介したパスを計算でき、計算が完全に失敗するのを回避できます。

  1. オブジェクトの周りに Anchored パーツを作成し、その CanCollide プロパティを false に設定します。

    Anchored part defining a region to apply a pathfinding modifier to
  2. パーツに PathfindingModifier インスタンスを挿入し、その PassThrough プロパティを有効にします。

    PathfindingModifier instance with PassThrough property enabled

    次に、ゾンビNPCからプレイヤーキャラクターへのパスが計算されると、パスはドアを超えて延長され、ゾンビにそれを通過させるように誘導できます。ゾンビがドアを開けることができなくても、ドアの後ろのキャラクターを「聞いている」ように反応します。

    Zombie NPC path passing through the previously blocking door

パスファインドリンク

時々、空間を横断することができない道を見つけ、渓谷などを横断し、次のウェイポイントに到達するためのカスタムアクションを実行する必要があります。これは PathfindingLink オブジェクトを通じて達成できます。

上記の島の例を使用すると、エージェントがすべての橋を渡るのではなくボートを使用させることができます。

PathfindingLink showing how an agent can use a boat instead of walking across all of the bridges

この例を使用して PathfindingLink を作成するには:

  1. 視覚化とデバッグを助けるために、3Dビューポートの右上隅の ビジュアライゼーションオプション ウィジェットから パス探索リンク を切り替えます。

  2. 2つの Attachments を作成し、1つはボートの座席で、もう1つはボートの着陸場所の近くです。

    Attachments created for pathfinding link's start and end
  3. ワークスペースに PathfindingLink オブジェクトを作成し、その 添付ファイル0 および 添付ファイル1 プロパティを、それぞれ開始と終了の添付ファイルに割り当てます。

    Attachment0/Attachment1 properties of a PathfindingLink PathfindingLink visualized in the 3D world
  4. 意味のある名前 like UseBoat をその Label プロパティに割り当てる。この名前は、パスファインドスクリプトでフラグとして使用され、エージェントがスタートリンクポイントに到達すると、カスタムアクションをトリガーします。

    Label property specified for PathfindingLink
  5. 含める Costs テーブル内の CreatePath() には、Water のキーと Label プロパティ名に一致するカスタムキーが含まれています。カスタムキーに 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
    }
    })
  6. ウェイポイントに到達したときに発動するイベントでは、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.LocalPlayer
    local character = player.Character
    local humanoid = character:WaitForChild("Humanoid")
    local TEST_DESTINATION = Vector3.new(228.9, 17.8, 292.5)
    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
    -- ウェイポイントラベルが「UseBoat」の場合はボートを使用し、そうでない場合は次のウェイポイントに移動する
    if waypoints[nextWaypointIndex].Label == "UseBoat" then
    useBoat()
    else
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    end
    else
    reachedConnection:Disconnect()
    blockedConnection:Disconnect()
    end
    end)
    end
    -- 最初は2番目のウェイポイントに移動(最初のウェイポイントはパスの開始; スキップする)
    nextWaypointIndex = 2
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    else
    warn("Path not computed!", errorMessage)
    end
    end
    function useBoat()
    local boat = Workspace.BoatModel
    humanoid.Seated:Connect(function()
    -- エージェントが座っている場合、ボートの移動を開始
    if humanoid.Sit then
    task.wait(1)
    boat.CylindricalConstraint.Velocity = 5
    end
    -- 島との関係で制約位置を検出する
    local boatPositionConnection
    boatPositionConnection = RunService.PostSimulation:Connect(function()
    -- 島の隣にボートを停止する
    if boat.CylindricalConstraint.CurrentPosition >= 94 then
    boatPositionConnection:Disconnect()
    boat.CylindricalConstraint.Velocity = 0
    task.wait(1)
    -- エージェントを解除して目的地に続行
    humanoid.Sit = false
    humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
    end
    end)
    end)
    end
    followPath(TEST_DESTINATION)

ストリーミング互換性

インエクスペリエンスの インスタンスストリーミング は、プレイヤーのキャラクターが世界中を移動すると、3Dコンテンツを動的に読み込み解除する強力な機能です。3D 空間を探検すると、スペースストリームの新しいサブセットがデバイスに流れ、既存のサブセットの一部が流出する可能性があります。

ストリーミング有効のエクスペリエンスで PathfindingService を使用するための次のベストプラクティスを考えてください:

  • ストリーミングは、キャラクタがその路径沿いに移動すると、指定されたパスをブロックまたはブロック解除できます。たとえば、キャラクターが森を通っている間、木がどこか前に流れてきて、道を妨げるかもしれません。パスファインドがストリーミングでスムーズに機能するには、ブロックされたパスの処理技術 を使用し、必要に応じてパスを再計算することを強くお勧めします。

  • パスファインドでの一般的なアプローチは、世界の既存の 宝箱モデル の位置にパス目的地を設定するなど、既存オブジェクトの座標を使用して 計算 することです。このアプローチは、サーバーがいつも世界全体を見ることができるため、サーバー側の Scripts と、クライアント上で実行される LocalScripts および ModuleScripts が、入力されないオブジェクトへのパスを計算しようとする場合に失敗する可能性があります。

    この問題を解決するには、BasePart モデル内の 位置 に目的地を設定することを検討してください。永続モデルは、プレイヤーが参加するとすぐにロードされ、ストリーミングされることはありませんので、クライアント側のスクリプトは PersistentLoaded イベントに接続して、イベントが発生した後に方位点を作成するモデルに安全にアクセスできます。