🧭

【Roblox】経路探索、ナビゲーションについて

2024/10/07に公開

はじめに

今回は、Robloxでの経路探索「Pathfinding」について紹介します。

Robloxバージョン 0.641.0.6410737

Pathfindingとは

Pathfindingとは、障害物や特定の領域を回避しながら、パスに沿ってキャラクターを移動させて目的地に到達させることができるプロセスです。
UnityのNavMeshなどを使用するNavgationシステムと似たものになります。

▼ 簡単な用語説明

用語 説明
Path 経路計算や計算結果などを保持するクラス。また、計算した経路がふさがった場合に呼ばれるイベントも設定できる。
PathWaypoint Pathで計算した結果を取得できるデータ型。座標、次のWaypointに到達するために必要なアクションなどを保持している。
PathfindingModifier 指定の領域にリージョンを設定できるオブジェクト。侵入させたくないエリアを作成可能。
PathfindingLink 侵入不可の領域を横切るパスを作成したい際に使用するオブジェクト。

Pathを作成 → 経路を計算 → PathWaypointから座標を取得 → 移動 → 次のPathWaypointに移動...
という感じで移動させる流れになります。

今回は上記を踏まえてPathfindingを使用して取得した座標から、キャラクターを移動させるプロセスを紹介します!

ナビゲーションの視覚化

Studioの3Dビューポートの右上隅から、Pathfindingの項目から表示できます。

パスを作成する

local PathfindingService = game:GetService("PathfindingService")

local path = PathfindingService:CreatePath({
	AgentRadius = 3,
	AgentHeight = 6,
	AgentCanJump = false,
	Costs = {
		Water = 20
	}
})

パスを作成するにはPathfindingService:CreatePath()を使用します。
引数にさまざまパラメータを設定することができ、それに従ったパスを作成できます。
下記が設定できるパラメータです。

Key デフォルト値 説明
AgentRadius integer 2 空きスペースを通過可能とみなすために必要な水平方向のスペースの最小量。
AgentHeight integer 5 空きスペースを通過可能とみなすために必要な垂直方向のスペースの最小量。
AgentCanJump boolean true パスファインディング中にジャンプを許可するかどうか。
AgentCanClimb boolean false パスファインディング中に TrussParts を登ることを許可するかどうか。
WaypointSpacing number 4 パス内の中間 Waypoint の間隔。
Costs table {} マテリアルまたは定義済みの PathfindingModifiers と、そのトラバースのコストの表。エージェントが特定のマテリアル / 領域を他のマテリアル / 領域よりも優先する場合に役立つ。

Costsについては、今回の記事にて後述いたします。

パスを作成したのみでは、経路を計算していません。
必ず次のパスの計算を行いましょう。

経路の計算

local success, errorMessage = pcall(function()
	path:ComputeAsync(start, finish)
end)

パスを計算するにはPath:ComputeAsync(start, finish)を使用します。
引数のstart, finishはそれぞれ開始地点の座標と終了地点の座標(Vector3型)です。
パスが作成できない環境の可能性もあるため、pcallで囲みましょう。

パス作成時に自動で呼ばれるわけではないので、パス更新時に必ず呼び出す必要あります。

PathWaypointの取得

local waypoints = path:GetWaypoints()

local nextPos = waypoints[1].Position
local nextAction = waypoints[1].Action
local nextLabel = waypoints[1].Label

パスの計算後はPath:GetWaypoint()で開始地点から終了地点までのPathWaypointをリストとして取得できます。

PathWaypoint.Positionで座標を獲得できます。
Humanoidの場合はMoveToメソッドで移動を行い、到着時はMoveToFinishedイベントで確認することで簡単にパスに沿って移動させることが可能です。

パスがブロックされた/解除された場合

blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
    -- ブロックされた
end)

unBlockedConnection = path.Unblocked:Connect(function(unblockedWaypointIdx)
    -- ブロックが解除された
end)

計算後のパスが何らかの理由でふさがってしまった場合はPath.Blockedイベント、
ブロックが解除された場合はPath.Unblockedイベントがそれぞれ呼ばれます。

ブロックされた際はパスの経路を再計算して、ブロックされた地点を避けるように移動させましょう。

パスファインディング修飾子

ここまでの内容でキャラクターを移動させることができると思います。
しかし、Path:ComputeAsync(start, finish)最短経路を計算します。
下記のように横断歩道を渡らせたい場合、何も設定しなければ斜めに横切るような経路になります。


赤ボールに向かうキャラクター(PathWaypointは黄色のボールで表現)

侵入させたくない領域や、計算で優先させたい領域を設定できるのがパスファインディング修飾子です。
主に2種類あります。
1つはマテリアルで分ける方法、もう1つはPathfindingModiferを使用する方法です。

マテリアルを使用する場合

Robloxはマテリアルがいくつも用意してあります。
このマテリアルを用いて、パスの計算に領域の優先度を設定できます。

local PathfindingService = game:GetService("PathfindingService")

local path = PathfindingService:CreatePath({
	Costs = {
		Water = 20,
		Neon = math.huge,
	}
})

パスを作成する際のPathfindingService:CreatePath()の引数のテーブル型に、Costsを設定することでマテリアル毎に通過させる優先度を設定できます。

Costsはマテリアル名と優先度を数値で設定します。
デフォルトは「1」です。値が低い方が優先度が高くなります。
math.hugeを設定することで通行、侵入不可の領域を設定することもできます。

設定できるマテリアル名は下記になります。
https://create.roblox.com/docs/reference/engine/enums/Material

PathfindingModiferを使用する場合

マテリアルでカバーできない場合があると思います。その場合はPathfindingModiferを使用します。
対応したいオブジェクトの子オブジェクトとして配置することで、効果を発揮します。


エクスプローラーの配置


PathfindingModiferのプロパティ

プロパティのLabelに設定した文字列を、path:ComputeAsync()の引数のCostsに設定することで優先度を設定できます。

そのオブジェクトがCanCollideでも通過する可能性がある場合はPassThroughをtrueにします。
扉や移動するオブジェクトの場合はtrueにしましょう。

ここまでで、パスに沿ってキャラクターを移動させ、領域・優先度の設定などを紹介しました。

次はPathfindingLinkを紹介します。
これは、移動不可の領域内で移動可能のパスを作成する際に利用できます。


PathfindingLinkのプロパティ

PathfindingLinkは下記を設定できます。
Attachment0 - 対象のパスの開始地点。
Attachment1 - 対象のパスの到着地点。
IsBidirectional - パスが両方向から通過できるか。
Label - 設定した文字列をpath:ComputeAsync()の引数のCostsに設定することで領域に優先度を設定する。

パスファインディング修飾子同様にLabelをCostsに設定することで、移動不可の領域内でもパスを作成することができます。

PathfindingLinkを使用することで上記のような例を作成できます。
先ほどは最短距離を進むだけでしたが、道路を通行不可にして横断歩道をPathfindingLinkで繋ぐことで、横断報道を通るようになりました!

ストリーミングとの互換性

Robloxモデルのロードやアンロードを管理するインスタンスストリーミングが存在します。
これにより、処理負荷を軽減したりモデルがロード中でもワールドに入ることができたりと、様々な恩恵を受けています。

  • モデルがロードされる前にパスを計算し、モデルがロードされた結果パスがブロックされた
  • 遠くの店まで移動させたいが、店モデルがロードされていないためパスの計算に失敗した

上記のような問題が起きかねないので、Pathfindingを使用する際はストリーミングとの互換性を重視しなければいけません。

モデルがブロックされる対策として、Path.Blockedイベントを設定しパスの再計算を行いましょう。
また、移動先のターゲットのモデルを永続モデル(Persistent)としてストリーミングすることでストリームアウトすることなく、パスの計算を可能にするという方法もあります。

Persistentを変更するにはプロパティのModelStreamingModeから変更できます。
詳しくは下記リンクからご確認ください。

まとめ

  • PathfindingService:CreatePath()でパスを作成できる。引数にパスの詳細や、パスを計算するときの領域の優先度を設定できる。
  • Path:ComputeAsync(start, finish)で開始地点から終了地点までのパスを計算することができる。
  • 計算結果はPathWaypoint型をテーブルで取得でき、座標を取得して目的地まで移動させることができる。
  • マテリアルやPathfindingModiferを使用することで、指定の領域を設定でき優先度を指定できるようになる。
  • PathfindingLinkを使用することで、通行不可の領域にパスをかけることが可能になる。
  • Pathfindingを使用する際は、ストリーミングとの互換性を重視する必要がある。

長くなりましたが、これで一通りPathfindingを紹介できたと思います。
経路探索は移動するNpcを作成したりと多くの場面で使用できると思います。
ぜひPathfindingを使用してみてください!!

参考

https://create.roblox.com/docs/characters/pathfinding
https://create.roblox.com/docs/reference/engine/classes/PathfindingService
https://create.roblox.com/docs/reference/engine/classes/Path
https://create.roblox.com/docs/reference/engine/datatypes/PathWaypoint
https://create.roblox.com/docs/reference/engine/classes/PathfindingModifier
https://create.roblox.com/docs/reference/engine/classes/PathfindingLink

ランド・ホー Roblox開発チーム

Discussion