地図/探索/エージェントAI

探索で選ぶ — 経路と影響

判断のもうひとつの柱が探索。今この一歩でなく、ゴールまでの道のりそのものを先に解く。

A* は、スタートからゴールまでの最短路を引く探索。各マスに「ここまでの実コスト g」と「ゴールまでの推定 h」を持たせ、その和 f = g + h が最小のマスから順に開く。h がゴールの方向を教えるので、全方向へ均等に広がる幅優先より細くゴールへ伸びる。h が実際の距離を超えなければ(許容的)最短路を保証する。マンハッタン距離(縦横移動の格子でちょうど許容的)がよく使われる。

格子の各マスに状態を持たせる。開いたマス(open)、確定したマス(closed)、塞がれた壁、最終経路の四つ。毎フレーム open から f が最小のマスを一つ取り出し、隣の四マスを見て、より小さい g で来られるなら親を記録して open へ入れる。ゴールに着いたら親をたどって経路を復元する。塞がれたマスは濃く、開いたマスを中明度、確定を淡く、復元した経路を黒で塗り分ける。

前線が壁を避けてゴールへ偏って伸び、着いた瞬間に黒い経路が一本残る。f でなく g だけで最小を取ると h が消えて、全方向へ均等に広がる幅優先になる。heuristic の戻り値を二倍三倍に膨らませると、ゴールへ一直線に突っ込んで探索は速くなるが、最短でない経路を引きうる。壁の密度 0.26 を上げると道が塞がって open が尽き、openLen === 0 で経路なしのまま打ち切る。

一匹なら A* でいい。同じゴールへ大群を向かわせるなら、全員ぶん A* を解くのは重い。ゴールから一度だけ距離を測って、各マスに「ゴールへ近づく向き」を焼いておく。

フローフィールド(流れ場)は、ゴールから幅優先で全マスの距離場を作り、各マスに「距離が一つ小さい隣へ向かう向き」を書き込んだもの。距離場の計算はゴール一点からの一回だけ。あとは何体のエージェントも、自分のいるマスの矢印を読むだけでゴールへ集まる。経路を個別に持たず一枚の場を共有するので、一対多のナビが探索一回ぶんで済む。

ゴールから幅優先で波を広げ、新しく到達したマスに「来た方向=距離が減る向き」を矢印として記録する。距離が無限大のまま(壁に囲まれて届かない)マスには矢印が立たない。格子点ごとにその矢印を短い線で引き、ゴールを黒く塗る。エージェントを撒いて、各自が足元のマスの矢印を読んで一歩進む。

矢印がゴールへ向かう流れの川になり、撒いた点が壁を回り込んで黒いマスへ吸い込まれる。点はゴールに着くか矢印のないマスに乗ると別の場所へ撒き直す。距離場は build の一回で作って固定し、各点は毎フレーム fdx, fdy を読むだけなので、点を 90 から増やしても計算量は撒いた数に比例して増えるだけになる。半透明の paper を毎フレーム上から塗っているので、点の通った跡が薄く尾を引く。

道を引くのとは別に、盤面全体の「どちらが優勢か」を場で持てる。各ユニットが周りへ影響を撒いて、味方はプラス、敵はマイナス。足し合わせた符号が、その地点を今どちらが支配しているかになる。

影響マップ(influence map)は、各ユニットから距離とともに減衰する影響値を撒いて格子に足し込んだ場。陣営ごとに符号を逆にすると、値の正負がその地点の支配勢力、値が 0 を横切る線が前線になる。減衰には距離の二乗に反比例する strength / d² をよく使う。この場を A* のコストに足し込むと、敵の濃い所を避ける臆病なルートも、突っ切る強気なルートも、混ぜる重み一つで連続的に作れる。

ユニットを画面に散らし、味方と敵で符号を分ける。壁で跳ね返りながら漂わせる。格子の各マスで全ユニットからの sign * strength / (距離² + soften) を足して場を作る。soften はユニット直上で値が発散しないための下駄。値の絶対値を明るさに写し、強い所ほど濃く塗る。隣のマスと符号が変わる境目(積が負)を黒で縁取ると、前線が浮く。

各ユニットの周りに濃い染みができ、味方と敵の染みがぶつかる谷間に黒い前線が走る。ユニットが動くと前線がぬるりと動き、味方が固まった側へ押し込まれる。strength / d² の二乗を一乗 strength / d に変えると影響が遠くまで届いて前線がぼやけ、soften を小さくするとユニット直上が鋭く尖る。隣との積が負のマスだけ黒くしているので、線は値がちょうど 0 になる等値線をなぞる。判断の材料が、点でなく一枚の場に乗る。