0からスタート!ゲーム開発ブログ

ゲーム開発に関する様々な記事を更新します

敵キャラのAIってどう実装するの?Unityでステルスゲームの敵AIを作る方法を解説!

はじめに

アクション系のゲームで開発難易度の高いものの一つが敵のAIです。 古典的なシューティングゲームであればあらかじめ敵の動きを決めておいてその通りに動かすだけでもよいのですが、 主人公キャラクターの動きに応じてふるまいを変えるような敵を登場させる場合には 何らかのAIを構築する必要があります。

本記事では筆者の販売した「盗賊少女」を例にしながら、 Unity の経路探索機能を利用したAIの作り方について紹介していきます。

敵の状態と状態遷移

このゲームに登場する敵は警備中・確認中・追跡中・探索中という4つの状態を持ち、下図のような状態遷移をします。

敵のAIを構築する場合、このような状態遷移図という図を作成して敵のふるまいを可視化することがあります。 以降の節で各状態について詳しく説明します。

警備中

敵の動きを決める簡単なやり方の一つは敵を予め決められた位置に配置しておき、その周辺をランダムに移動することです。しかしこれでは敵の動きを予測することがかなり困難になり、パズル的な要素や戦略的な要素が弱くなってしまいます。 そこで、「盗賊少女」ではツクール系のように敵の動きをエディタで設定できるようにしました。具体的には下記のような設定が出来るようにしています。

  • 敵が移動する地点をリストにして持たせる
  • 敵の移動パターンを設定する
    • 初期位置から動かない
    • 地点間をランダムに移動する
    • 地点を順に移動する(往復)
    • 地点を順に移動する(巡回)
  • 目標地点に到着してから次の地点に移動するまでの時間を設定する
    • 時間の最小値と最大値とを設定して、実際の待ち時間は最小?最大値の範囲でランダムに決定する

このように敵の動きを事前に決めておくことで、敵がどのように動くのかを予測し、戦略を立てて遊べるようになります。さらにステージを作成する側も敵の移動経路を決めておくことで、主人公が安全に移動できる経路を確保しておく、すなわちステージの攻略パターンを確保することができるようになります。これはステージ設計者とゲームを遊ぶプレイヤーとの勝負でもあります。

確認中

警備中の状態で主人公が視界に入り、一定時間経過する前に視界から主人公が外れた場合、主人公がいた位置を確認ポイントに設定します。 また、敵の近くで何かの音が発生した場合もその音の位置を確認ポイントに設定します。 いずれかの理由で確認ポイントが設定された場合、敵は確認ポイントに向かって移動します。 確認ポイントに到着して主人公を発見できなければ警備中に戻り、移動経路の移動を再開します。

追跡中

警備中や確認中・探索中に主人公が一定時間敵の視界に入ると敵は追跡中になります(探索中の場合は一瞬で追跡中になる)。 また、いずれかの敵が追跡中になるとすべての敵が一斉に追跡中に変化します。 追跡中はつねにすべての敵で確認ポイントが共有されるのが特徴で、主人公に向かって敵が一斉に集まってきます。

探索中

探索中において敵は移動せず、その場であたりを見回します。 一定時間見回し続けた後は警備中に戻ります。

経路探索

以上のように4つの状態を遷移しながら敵はフィールド上を移動しますが、フィールド上には様々な障害物があるため、これらの障害物を避けながら進む必要があります。 とはいえ、障害物にはさまざまのものがあり、目標地点へ最短経路で進むための経路を求めることは簡単ではありません。

Unity にはNav Mesh という機能があり、Nav Mesh を使うことで障害物を避けながら最短経路で目標地点へ敵を移動させることができます。 本記事ではNav Mesh の詳細な使い方というよりは、Nav Mesh を使うとどのようなことができるのかに主眼を置いて説明していきたいと思います。

敵AIに通過させなくするための障害物を設定できる

床や壁のようにAIに干渉するオブジェクトはStaticフラグを「Navigation Static」としましょう。 これによって後述の経路マップ自動生成が有効となります。

そして、Navigatrion タブのObject 欄より床オブジェクトに対してはNavigation Area をWalkable に、 壁や障害物などのオブジェクトに対してはNavigation Area をNot Walkable に設定します。

そして、Navigation タブのBake (ベイク)を実行すると、敵AIが障害物を避けて移動するための経路マップが生成されます。

シーンタグを開くとBake された状態を確認することができます。下図の例の場合、敵AIが移動できる領域は青い領域となります。

ここでのポイントは経路探索において障害物として扱うオブジェクトを見た目と関係なく設定できることです。 例えば、小物のような小さなオブジェクトに対してまでStatic フラグの設定をしてしまうと、パッと見通過できそうな通路であっても障害物があると判定され、敵AIが通過できなくなってしまう現象も発生する可能性があります。このように、オブジェクトとしては存在するが経路探索の邪魔をしてほしくないようなオブジェクトには意図的にStatic フラグの設定をしないようにすることもできます。

実際に障害物をよけながら敵AIを動かすときはNav Mesh Agent を使用します。 敵オブジェクトにNav Mesh Agent コンポーネントを割り当て、

以下のようなコードを記述すると、敵AIが障害物を避けながら確認ポイントに向かって移動します。

NavMeshAgent enemy = GetComponent<NavMeshAgent>(); 
Vector3 pos = (確認ポイントの座標);
enemy.SetDestination(pos);

敵の種類に応じて通過可能な段差の高さや身長を変えることができる

Navigation タグのAgents を設定することで、敵の種類を複数定義し、敵の種類ごとに水平方向の半径(Radius)や身長(Height)、乗り越えられる段差の高さ(Step Height)や上ることができる坂の角度を設定することができます。

「盗賊少女」では通常の敵よりも鳥系の敵に対してStep Height の値を大きくすることで、通常の敵では通過できない低い壁を鳥系の敵に通過させるような工夫を取り入れています。

Bake しなくても敵AIの移動経路を求めることができる

上記のやり方を採用する場合、ステージごとにBake しなければなりません。つまり、ステージの数だけUnity のシーンを作成しなければなりません。これはとても面倒です。 少し応用編になりますが、実はUnity 公式から下記のような環境が公開されています。

https://github.com/Unity-Technologies/NavMeshComponents

この環境に含まれるBuildNavMesh コンポーネントとNavMeshSurface コンポーネントを利用すると以下のような流れにより一つのシーンで経路マップ付きのステージを多数作成することが可能になります。

  1. ステージ番号からステージデータを特定して読み込む
  2. ステージデータを元にステージのモデリングデータを構築する
  3. NavMeshSurface が経路マップを構築する

ある程度Nav Mesh を扱えるようになったらこのような応用テクニックにも手を出すと本格的でメンテナンス性に優れたゲームアプリを作成できるようになります。 是非挑戦してみてはいかがでしょうか?

敵の種類に応じて経路マップを変化させることができる

先ほど説明したNavMeshSurface を使用するとさらに敵の種類に応じて移動経路を変化させることができるようになります。

例えば通常の敵の場合は床(Floor)の上を移動し、壁(Wall)や障害物(HardBlock)を通過できません。

通常の敵の移動可能な経路領域(青部分)は下記のようになります。

一方、幽霊系(Goast)の敵の場合は壁や障害物の影響を無視して床(Floor)の上を移動できます。

幽霊系(Goast)の敵の移動可能な経路領域(青部分)は下記のようになります。

障害物ごとに異なるレイヤ(Floor, Wall, HardBlock)を設定し、敵の種類に応じてNavMeshSurface を個別に使用することで、 このような使い分けが可能となります。 これもまた応用テクニックの一つになりますが、うまく利用することでより効率的なレベルデザインができるはずです。

終わりに

本記事では敵AIの考え方とNavMesh を使ってできることについて軽く紹介しました。

NavMesh を使用すると障害物を避けながら最短経路で移動するような敵AIを実現することができるようになります。 また、若干複雑にはなりますが、敵の種類に応じて乗り越えられる段差の高さや身長、障害物のON/OFF を切り替えることができるようになります。 最初は障害物を避けながら目標地点に移動するだけのデモを作るのがオススメです。 徐々にAIを拡張していくことで本格的なゲームになっていくことでしょう。