Unityに限らず、ゲームを作っていると多くの場合で「敵キャラクター」が必要になる場合があります。
一口に敵キャラクターといっても様々だと思いますが、中でも「AIで状況を判断して自動行動する敵キャラクター」は特に需要が高いと思われます。しかしこのような敵キャラは作るのが比較的難しいため、どうやって作ればいいのか分からないという方も多いのではないでしょうか。
そこで今回は3Dゲーム用の敵キャラを例に挙げて、簡単な敵AIの作り方を説明していきますね。
敵キャラクターに必要な機能
では、はじめに「ゲームの敵キャラクター」に必要な機能を整理していきましょう。前提として次のようなゲームに使う敵キャラについて考えるものとします:
- 3Dゲーム
- アクション性の高いゲーム
そうすると主に必要な機能としては次の2項目を挙げられます。
- 経路探索:
(例)障害物に引っかからずにプレイヤーを追いかける - 状況判断:
(例)プレイヤーを見つけたら攻撃する
経路探索
まず一つ目は経路探索です。アクション性の高いゲーム(特にアクションゲーム・FPS・TPS等)では、敵キャラクターが適切に動かなければゲームが面白くなりません。そこで障害物を上手く避けて目的地に移動できるようにする必要があります。
経路探索の処理は自力で実装しようとすると難しいジャンルですが、最近のゲームエンジンではその辺を手軽に解決できるAPIが提供されている(UnityならNavmeshAgent等)のでそれを使えばOKでしょう。
状況判断
次に二つ目は状況判断です。アクション性の高いゲームでは敵キャラクターとの戦闘を行う場面がよくあると思いますが、そこで上手く状況を判断して立ち回るAIが求められます。
状況判断を行うAIの種類は色々あり、実際にどれを使えばよいのかはゲームによって異なります。状況判断によく使われるAIは次の通りです。
- ルールベースAI:設定された条件によって判断
- ステートベースAI:状況に合った状態(ステート)を実行
- ビヘイビアベースAI:行動をツリー化し、そのツリーをたどって判断
- ユーティリティベースAI:行動の見返りを計算して、最も多くの見返りのある行動を実行
Unityでの敵キャラクターの具体的な作り方
では、ここから敵キャラクターの作り方をもう少し具体的に見ていきましょう。Unityで敵キャラクターを実装する方法は次の通りです。
- 経路探索:NavMeshAgentを使う
- 状況判断:ステートベースAIを実装する
経路探索:NavMeshAgent
まず経路探索については、Unityに「NavMeshAgent」という経路探索コンポーネントが用意されているのでそれを使いましょう。これはなかなか癖が強い機能ではありますが、上手く使えば手軽に経路探索を行うことができます。
NavMeshAgentの使い方は姉妹サイトの「ナビメッシュエージェント(NavMeshAgent)の使い方」で詳しく説明しているので、そちらをご覧いただければと思います。
状況判断:ステートベースAI
さて次は状況判断の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のメインルーチン
そうしたら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ステートに遷移し、その後の判断は行いません。
行動実行
あとは実際の行動処理を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を導入できるアセットがいくつか販売されています(※代表的なものとしては下記の「Behavior Designer」があります)。柔軟な状況判断ができるAIを作りたい方はストアでチェックしてみてください。
おわりに:敵キャラクターを自作してゲームを面白くしよう
以上、Unityでの敵AIの作り方を紹介しました。
面白いゲームを作るためには敵キャラクターを上手く作ることは欠かせません。ぜひ今回の内容を足掛かりとして敵キャラの作り方を研究してみてください。