【Unity】敵AIの作り方!自動で行動する敵キャラクターを作ってみよう

AIを持つ敵キャラを作ろう Unity

Unityに限らず、ゲームを作っていると多くの場面で「敵キャラクター」が必要になる場合があります。

一口に敵キャラクターといっても様々だと思いますが、中でも「AIで状況を判断して自動行動する敵キャラクター」は特に需要が高いと思われます。しかしこのような敵キャラは作るのが比較的難しいため、どうやって作ればいいのか分からないという方も多いのではないでしょうか。

そこで今回は3Dゲーム用の敵キャラを例に挙げて、Unityでの敵AIの作り方を説明していきますね。

敵キャラクターのAIに必要な機能

では、はじめにゲームの敵AIに必要な機能を整理していきましょう。一口に敵AIといっても色々なパターンが考えられるのですが、ここでは前提として次のようなゲームに使う敵キャラのAIについて考えるものとします:

  • 3Dゲーム
  • アクション性の高いゲーム

そうすると敵AIに必要な機能としては次の2項目が挙げられます。

  1. 経路探索(ナビゲーションAI):
    (例)障害物に引っかからずにプレイヤーを追いかける
  2. 状況判断(キャラクターAI):
    (例)プレイヤーを見つけたら攻撃する

経路探索(ナビゲーションAI)

まず一つ目は経路探索を行うナビゲーションAIです。アクション性の高いゲーム(特にアクションゲーム・FPS・TPS等)では、敵キャラクターが適切に移動しなければゲームが面白くなりません。そこで障害物を上手く避けて目的地に移動できるようにする必要があります。

経路探索の処理は自力で実装しようとすると難しいジャンルですが、最近のゲームエンジンではその辺を手軽に解決できるAPIが提供されている(UnityならNavmeshAgent等)のでそれを使えばOKでしょう。

状況判断(キャラクターAI)

次に二つ目は状況判断を行うキャラクターAIです。アクション性の高いゲームでは敵キャラクターとの戦闘を行う場面がよくあると思うのですが、そこで状況を適切に判断して上手く立ち回るAIが求められます。

キャラクターAIのアルゴリズムは色々なものが考案されており、実際にどれを使えばよいのかはゲームによって異なります。キャラクターAIによく使われるアルゴリズムは次の通りです。

  • ルールベースAI:設定された条件によって判断
  • ステートベースAI:状況に合った状態(ステート)を実行
  • ビヘイビアベースAI:行動をツリー化し、そのツリーをたどって判断
  • ユーティリティベースAI:行動の見返りを計算して、最も多くの見返りのある行動を実行

ルールベースAI

ルールベースAIはもっとも原始的なキャラクターAIで、予め設定された条件をもとにして判断を行い・行動するAIです。実装は簡単ですが敵の行動パターンが単調になりやすい上、複雑な条件を設定しようとすると管理が大変になるというデメリットがあります。

ステートベースAI

次にステートベースAIはルールベースAIを改良したキャラクターAIで、一つの行動を「ステート」と呼ばれる状態に紐づけて管理します。

ステートベースAIの例

身近なところでいうとUnityのAnimatorは(AIではないですが)まさにステートベースなので、それを思い出して頂ければ分かりやすいかと思います。

ステートベースAIは比較的簡単に実装できてそこそこ複雑な思考も実現できるため、実用的かつ個人ゲーム開発でも導入しやすいと言えるでしょう。ただしこちらもステートの数が多くなると管理が大変になるといったデメリットがあります。

ビヘイビアベースAI

ビヘイビアベースAIはキャラクターの行動や判定条件を「ビヘイビア」という単位にまとめてツリー化するキャラクターAIです。

ビヘイビアベースAIの例

実装は難しいのですが、条件を整理しやすく・かつ賢いAIを作りやすいので商業ゲーム等によく使われているAIとなります。リアル志向のFPS/TPSなど、より高度な敵AIが必要なゲームではこのAIを使うとよいでしょう。

ユーティリティベースAI

最後にユーティリティベースAIは、各行動の見返りの大きさを状況に応じて採点し、最も得点の高い行動を選択するキャラクターAIです。実装自体は手軽にできる反面、「採点基準や計算式をどうやって決めるか?」という課題があります。

Unityでの敵AIの具体的な作り方

では、ここからUnityでの敵AIの作り方をもう少し具体的に見ていきましょう。Unityで敵AIを比較的簡単に実装する方法は次の通りです。

  • 経路探索:NavMeshAgent等を活用する
  • 状況判断:ステートベースAIを実装する

経路探索:NavMeshAgent等を活用する

まず経路探索については、Unityに「NavMeshAgent」という経路探索機能が標準で用意されているのでそれを使うとよいでしょう。これは結構癖が強い機能なのですが上手く使えば手軽に経路探索を行うことができます。

NavMeshAgentの基本的な使い方は姉妹サイトの「ナビメッシュエージェント(NavMeshAgent)の使い方」で詳しく説明しているので、そちらをご覧いただければと思います。

なお簡単な経路探索であればNavmeshAgentで十分かもしれませんが、より高度・高速な経路探索を行いたい場合は下記のアセット「A* Pathfinding Project Pro」が非常に強力です(※私はもうNavmeshAgentは使っておらず、こちらを長年愛用しています)。プロジェクトに応じて経路探索のアルゴリズムを変更できるほか2Dの経路探索にも対応していて便利なので、興味のある方はぜひチェックしてみてください。

状況判断:ステートベースAIを実装する

さて次は状況判断のAIの作り方です。前述の通り状況判断のアルゴリズムの種類は色々ありますが、ここでは汎用性が高くて実装が簡単な「ステートベースAI」の作り方を紹介します。

※なおすべてのコードを掲載すると記事がとんでもない長さになってしまうため、ここでは重要な部分だけをピックアップして説明しています。そのままコピペしても動かないのでご注意ください。

ステートの作成

まず、列挙型を使って必要なステートを作成します。

public enum EnemyAiState
{
  WAIT,			//行動を一旦停止
  MOVE,			//移動
  ATTACK,		//停止して攻撃
  MOVEANDATTACK,	//移動しながら攻撃
  IDLE,			//待機
  AVOID,		//回避
}

public EnemyAiState aiState = EnemyAiState.WAIT;

AIの準備

次にAIの準備やメインルーチンの処理を行う関数を用意します。

void SetAi()
{
  if (isAiStateRunning)
  {
    return;
  }

  InitAi();
  AiMainRoutine();

  aiState = nextState;

  StartCoroutine("AiTimer");
}

InitAI関数はAIの初期化を行う関数とします。また、AiTimer関数は思考のインターバルを設定するコルーチンとします(※実際のコードはここでは省略します)。

AiTimerが動作している間はisAiStateRunningがTrueになるようにすると、その時は処理が実行されなくなります。このようにしているのは毎フレーム思考ルーチンを走らせる必要がないからです。

もちろん毎フレーム思考ルーチンの処理を行うようにすればAIの反応速度は速くなりますが負荷も高くなります。したがって敵の数にもよりますが一般的には0.1~0.5秒に一回くらいのペースで処理を行うのが適切かと思います。

AIのメインルーチン

そうしたらAIのメインルーチンをキャラクターごとに書きます。例えば

  • 普段は適当に移動
  • プレイヤーがshootDistanceよりも近づいたら移動しながら攻撃

という行動をとる敵の場合なら次のようになります。

void AiMainRoutine()
{
  if (wait)
  {
    nextState = EnemyAiState.WAIT;
    wait = false;
    return;
  }

  if (enemyCanShoot && isChasing && distance < shootDistance)
  {
    nextState = EnemyAiState.MOVEANDATTACK;
  }
  else
  {
    nextState = EnemyAiState.MOVE;
  }
}

ここでwaitは思考ルーチンを強制的に停止させるフラグです。停止フラグが立った場合はWAITステートに遷移し、その後の判断は行いません。

ちなみにメインルーチンは上記のようにC#スクリプトにベタ書きすることもできますが、これだとメンテナンス性はかなり悪いです。そこでScriptableObjectに判定条件を設定しておき、それを読み込んで判定を行う方法もあります。具体的なやり方は省略しますがご興味のある方はぜひご自身で研究してみてください。

行動実行

あとは実際の行動処理をUpdate等から呼び出せばOKです。

void UpdateAI()
{
  SetAi();

  switch (aiState)
  {
    case EnemyAiState.WAIT:
      Wait();
      break;
    case EnemyAiState.MOVE:
      Move();
      break;
    case EnemyAiState.ATTACK:
      Attack();
      break;
    case EnemyAiState.MOVEANDATTACK:
      MoveAndAttack();
      break;
    case EnemyAiState.IDLE:
      Idle();
      break;
    case EnemyAiState.AVOID:
      Avoid();
      break;
  }
}

このようにすることでステートベースAIを実装することができます。

余談:より高度なAIを実装するには…

最後に余談として、アセットストアにはもっと高度なビヘイビアベースAIを導入できるアセットがいくつか販売されています(※代表的なものとしては下記の「Behavior Designer」があります)。より柔軟な状況判断ができるAIを作りたい方はストアでチェックしてみてください。

おわりに:敵キャラクターを自作してゲームを面白くしよう

以上、Unityでの敵AIの作り方を紹介しました。

面白いゲームを作るためには敵キャラクターを上手く作ることは欠かせません。ぜひ今回の内容を足掛かりとして敵キャラのAIの作り方を研究してみてください。

この記事がUnityでのゲーム開発のお役に立てば幸いです。