【Roblox】経路探索、ナビゲーションについて
はじめに
今回は、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
を設定することで通行、侵入不可の領域を設定することもできます。
設定できるマテリアル名は下記になります。
PathfindingModiferを使用する場合
マテリアルでカバーできない場合があると思います。その場合はPathfindingModiferを使用します。
対応したいオブジェクトの子オブジェクトとして配置することで、効果を発揮します。
エクスプローラーの配置
PathfindingModiferのプロパティ
プロパティのLabelに設定した文字列を、path:ComputeAsync()
の引数のCostsに設定することで優先度を設定できます。
そのオブジェクトがCanCollideでも通過する可能性がある場合はPassThroughをtrueにします。
扉や移動するオブジェクトの場合はtrueにしましょう。
PathfindingLink
ここまでで、パスに沿ってキャラクターを移動させ、領域・優先度の設定などを紹介しました。
次は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を使用してみてください!!
参考
当社ではRobloxを活用したゲームの開発、 また企業の商品やサービスの認知度拡大に寄与する3Dワールドの制作など、 Robloxにおける様々な活用支援を行っております。 Robloxのコンテンツ開発をご検討されている企業様は、お気軽にご相談ください。 landho.co.jp/
Discussion