ステートマシンとBehavior Tree — 敵AIは、毎Tick何を考えているのか
ゲームの敵は、ふだんは決まった道を巡回し、プレイヤーを見つけると追いかけ、近づけば攻撃し、やられそうになると逃げ出します。まるで考えているように見えますが、やっているのは「毎Tick、次にどの行動をするかを選ぶ」ことだけです。
その「選び方」の代表的な書き方が、ステートマシン(FSM)とBehavior Tree(BT)です。下の図1で、青い丸(あなた)をドラッグして敵に近づいたり離れたりしながら、敵の頭の中で何が調べられているかを見てください。
ステートマシン — 「今どの状態か」を覚えて、矢印で移る
ステートマシン(FSM: 有限状態機械)は、敵が今いる状態を1つだけ覚えておき、状態と状態のあいだに「この条件がそろったら移る」という矢印を引いておく書き方です。巡回中なら「見つけた?」「HPが低い?」の2本だけを調べ、どちらも成り立たなければ巡回のまま。図1で敵の視界に入ると、矢印が光って状態が切り替わります。
考えることが「今の状態から出る矢印」に限られるので、小さくて読みやすく、処理も軽いのが長所です。攻撃中の敵は「見えるか」をもう調べません。調べる必要がない、とわかっているからです。
困るのは、どの状態からでも起こりうる行動です。「HPが低ければ逃げる」は、巡回中でも追跡中でも攻撃中でも割り込みたい。だから図1では、同じ「HP低い」の矢印を3本引いています(オレンジの点線)。
Behavior Tree — 毎回、上から順に聞き直す
Behavior Tree は、状態を覚える代わりに優先順位つきの「やることリスト」を木の形で持ちます。毎Tick、根っこの Selector が上の枝から順に試し、条件が成り立った最初の枝の行動を実行します。枝の中の Sequence は「条件を調べて、成功したら行動する」という左から右への並びです。
「HPが低ければ逃げる」はいちばん上の枝に1回書くだけ。毎Tick必ず最初に調べられるので、何をしていても割り込めます。どの条件も成り立たなければ、最後の「巡回する」まで落ちてきます。条件のない最後の枝が、いつでも成功する受け皿になっているわけです。
行動が増えると — 矢印は掛け算、枝は足し算
2つの違いがはっきり出るのは、行動を増やしたときです。下の図2で行動の数を増やしてみてください。FSMでどの行動からどの行動へも移れるようにすると、新しい状態を1つ足すたびに既存のすべての状態との間に矢印が要ります。BTなら、優先順位の位置に枝を1本差し込むだけです。
どちらを使うか
BTがいつでも上、というわけではありません。状態が少なく、移り方がはっきり決まっているもの(ドアの開閉、キャラのアニメーションの切り替えなど)はFSMのほうが素直で、Unityのアニメーション設定もステートマシンです。一方、行動が多く、優先順位で割り込ませたい敵AIにはBTがよく使われ、Unreal Engineには標準で組み込まれています。実際のゲームでは、状態を入れ子にした階層型ステートマシンや、BTの葉の中で小さなFSMを動かすなど、組み合わせて使うのがふつうです。
- ステートマシン(FSM)
- 「今の状態」を1つ持ち、条件がそろったら矢印(遷移)にそって別の状態へ移る仕組み。
- 遷移
- 状態から状態への矢印。「見つけた」「HPが低い」などの条件がついている。
- Behavior Tree(BT)
- 行動を優先順位つきの木で表し、毎Tick根っこから評価し直して行動を選ぶ仕組み。
- Selector
- 子を上から順に試し、どれか1つが成功した時点で成功になるノード。「どれかをやる」。
- Sequence
- 子を左から順に試し、全部成功して初めて成功になるノード。「条件を確かめてから、やる」。
まとめ
敵AIは、毎Tick「次にどの行動をするか」を選んでいるだけです。FSMは今の状態を覚えて、そこから出る矢印だけを調べるので小さく軽い反面、どこからでも割り込む行動があると同じ矢印をあちこちに引くことになります。BTは状態を覚えず、毎回、優先順位の高い枝から聞き直すので、行動を足しても枝が1本増えるだけ。「覚えておいて移る」か「毎回聞き直す」かが、2つを分ける核心です。