Pathfinding & decisions

Funnel Algorithm / String Pulling

ファネル法

ナビメッシュの通過回廊の内側で折れの少ない短い経路を求める。

4 / 10

Interactive demo

Preparing the demo…

Funnel Algorithm / String Pulling

Drag to orbit, Ctrl/⌘ + scroll to zoom. Use two fingers on touch screens.

This explanation is currently available in Japanese.

ひとことで

ナビメッシュの探索で見つかった「通る多角形の列(回廊)」の中で、角をかすめて進む最短の折れ線を求める方法です。回廊の入口と出口の間に糸を張って、両端を引っ張ったときにできる形なので、ストリングプリング(糸を引く)とも呼ばれます。多角形の中心や辺の中点を結ぶだけのジグザグの経路に比べて、短く、人が歩きそうな自然な経路になります。

ゲームでの使いどころ

  • ナビメッシュの経路の仕上げ:Recast/Detour などのナビメッシュのライブラリは、A* で回廊を求めたあと、ほぼ必ずこの処理で経路を引く。
  • 曲がり角の数を減らす:曲がり角が少ないほど、キャラクターの向きの変化が少なく、自然に見える。
  • 次の曲がり角だけを求める:毎フレーム全体の経路を作り直さず、「今いる位置から見た次の曲がり角」だけを求めて、そこへ向かって歩く(シーク)。

しくみ

回廊の境目の辺(ポータル)を、進む向きに対して左端と右端をそろえて並べます。始点を「頂点(apex)」として、頂点からポータルの左端・右端へ 2 本の線を張ります。これが漏斗(ファネル)です。

ポータルを 1 つずつ進めながら、次のように漏斗を狭めていきます(Mononen の Simple Stupid Funnel Algorithm)。

  1. 狭める:次のポータルの右端が、今の右の線より内側にあれば、右の線をそこまで動かします(左も同様)。
  2. 越えたら曲がる:右の線を動かそうとして、それが左の線を越えてしまう場合は、左の線の端が「どうしても回り込まなければならない角」です。そこを曲がり角として経路に加え、新しい頂点にして、その角の次のポータルからやり直します(左右逆も同様)。
  3. 最後のポータル(ゴール)まで進んだら、ゴールを経路に加えて終わりです。

内側か外側かの判定は、2 次元の外積の符号で行います。頂点 a\mathbf{a} から見て点 c\mathbf{c} が線 a→b\mathbf{a} \to \mathbf{b} の左にあるかは

(b−a)×(c−a)=(bx−ax)(cy−ay)−(by−ay)(cx−ax)>0(\mathbf{b} - \mathbf{a}) \times (\mathbf{c} - \mathbf{a}) = (b_x - a_x)(c_y - a_y) - (b_y - a_y)(c_x - a_x) > 0

でわかります。平方根も三角関数も使わないので、とても速く動きます。

実装のポイント

  • ポータルの左右:回廊の多角形の中から辺を見て、左手にある端を left にそろえます。そろっていないと漏斗がすぐ裏返って、おかしな経路になります。
  • 最初と最後:始点とゴールを、左右の端が同じ「長さ 0 のポータル」として、列の最初と最後に足しておくと、処理を分けずに済みます。
  • 角から少し離す:ナビメッシュをキャラクターの半径で縮めていれば、角をかすめてもめり込みません。縮めていない場合は、曲がり角を外へ少しずらします。
  • なめらかにする:折れ線の角を、キャットマル・ロム曲線や円弧でならすと、さらに自然な歩き方になります。

つまずきやすい点

  • 同じ点の扱い:頂点と端が同じ位置にあるとき(直前に曲がった角など)は外積が 0 になります。この場合も狭めてよいことにしないと、処理が進まなくなります。
  • やり直しの位置:曲がり角を見つけたら、その角があったポータルの次から調べ直します。今のポータルから続けると、角を 1 つ見落とします。
  • 回廊が最短とは限らない:ファネル法は、与えられた回廊の中での最短を求めます。A* が遠回りの回廊を選んでいれば、経路もそれに従います。

デモで試すこと

  1. 白い点が頂点、青い線と黄色い線が漏斗の左右の線、太い白線が今見ているポータルです。ポータルが進むにつれて漏斗が狭まっていく様子を見てください。
  2. 片側の線が反対側を越えたところで、その角が曲がり角として赤い経路に加わり、頂点が移ります。
  3. 「ポータルの中点を結んだ経路と比べる」で、紫の点線(ジグザグの経路)との長さの違いを、左下の数値で確かめてください。
  4. 「1 手順の時間」を長くすると、1 つずつ追えます。床をクリックすると、別の目的地で最初から始まります。

References

Red Blob Games — A* Introduction ↗

A general reference for this subcategory.