公開日:2023/09/27  更新日:2023/09/27

Unityの敵AIの作り方

 前回までの記事で敵が視界を持ち、Playerを見つけると徘徊行動→Playerへ近づく行動に変化するようにした。


 前回までの記事↓
TPSのキャラ移動
ジャンプ
ダッシュ実装
自作キャラを動き回らせる
剣を振る方法
剣コンボ
足が浮く対策
剣の軌跡の作り方
燃える剣の必殺技
ステータス管理
ダメージ判定・処理
HPゲージ
敵の攻撃
メッシュ変形での範囲攻撃
ビーム攻撃
NavMeshの使い方
敵の徘徊
視界の判定

 だが、敵がPlayerに突っ込んでいくだけでは面白くない。そこで、距離をとって遠距離攻撃を仕掛けたり左右から回り込むなど様々な行動をとる賢いAIを作り方を紹介していく。

Unityでの敵AIの作り方

ステータス記述
敵にとってほしい行動パターンを実現する
実際のスクリプト記述
目次にもどる

ステータス記述

 AIの作り方は実に様々だ。
 今回は敵であるスケルトンをとってほしい行動を考え、プレイヤーからの距離を元にして以下の4行動パターンを作成し、4パターンのうち、どの行動を多くとるかを優先度のパラメータを作って決めていく。

 ①近づいて近接攻撃

 ②距離をとって遠距離攻撃

 ③Playerの左右に回り込んで近接攻撃

 ④Playerの左右に回り込んで遠距離攻撃

 そのために必要なステータスをScriptableObjectを使って作っていく。
 なお、ScriptableObjectがわからない場合は以下の記事で紹介しているので確認してほしい。
ステータス管理

 遠距離攻撃不可距離
 遠距離攻撃を撃たない近距離。名前はLongAttackdameとした。

 移動速度
 プレイヤーを見つけた際の移動速度。名前はIdouSpeedとした。

 移動速度
 プレイヤーを見つけた際の移動速度。名前はIdouSpeedとした。

 回転速度
 プレイヤーに向く速度。名前はIdouKaitenとした。

 行動継続時間(最低)
 敵が同じ種類の行動をとる最低時間。名前はKoudoukeizokuminとした。

 行動継続時間(最高)
 敵が同じ種類の行動をとる最高時間。名前はKoudoukeizokumaxとした。

 近づいて近接攻撃優先度
 プレイヤーに最短距離で近づいて近接攻撃する行動パターンの優先度。名前はKinnkyorihindoとした。
 離れて遠距離攻撃優先度
 プレイヤーから距離をとって遠距離攻撃する行動パターンの優先度。名前はEnkyorihindoとした。

 左右回り込み近接攻撃優先度
 プレイヤーの左右どちらかの側面へ移動し近接攻撃する行動パターンの優先度。名前はMawarikomikinnsetuyuusenとした。
 左右回り込み遠距離攻撃優先度
 プレイヤーの左右どちらかの遠距離側面に移動し遠距離攻撃を仕掛ける行動パターンの優先度。名前はMawarikomikinsetuyuusenとした。

目次にもどる

敵にとってほしい行動パターンを実現する。

 以下の4つの行動パターンをどう実現するかについて紹介していく。

 ①近づいて近接攻撃

 ②距離をとって遠距離攻撃

 ③Playerの左右に回り込んで近接攻撃

 ④Playerの左右に回り込んで遠距離攻撃

 ①については以前の記事でやっているようにプレイヤーの位置をNavMeshで指定して目的地にしてやれば問題ない。

 「②距離をとって遠距離攻撃」は厄介だ。プレイヤーから離れた位置を目的地として指定すればいいのだが、この行動をとる直前にスケルトンがプレイヤーの近くにいた場合、以下のようにプレイヤーに背を向けて移動するので不自然だ(デフォルトではNavMeshで移動させると目的地へ向く)。
 そこでプレイヤーを発見した後は攻撃中以外はプレイヤーの方向を常に向き続けるようにする(攻撃直前まではプレイヤーの方向を向き続けるので、攻撃の照準をあわせる仕様にもなる)。スケルトンのNavMesh Agentのコンポーネントを見てほしい。
 「Angular Speed」の項目は目的地へ向く回転速度を示す。この値を0にしてやることで目的地へ向き変更をしなくなる。

 その上で、スクリプトでプレイヤーのほうを向き続けるようにしていく。だがこれも簡単そうで結構難しい。これを可能にするにはQuaternionを使う必要がある。
 Quaternionとは回転情報を扱う関数である(概念を全て理解するのは特に初心者には難しいが、これだけは覚えておこう)。
 ちなみにTransformコンポーネントのRotationにゲームオブジェクトの回転が記述されているが、この値はX軸、Y軸、Z軸のオイラー角(度数法、1周を360度としており人間にもわかりやすい)。

 だが、ここに入力したオイラー角をUnityはQuaternionとして計算して持っている。
 以下の記述をUpdateメソッドなどに入れることでスケルトンがプレイヤーの方向を向き続けることが可能。
 var qrot = Quaternion.LookRotation(PlayerPosition - ThisPosition);でPlayerPositionとThisPositionの間のQuaternionでの角度を求めることができる。
 後はQuaternion.Slerpを使うことで、プレイヤーの方向へ滑らかに向くことができる。今の角度、向きたい角度、向く速度を記述すればOK(Time.timeをかけることでフレーム数が多ければ1回の処理の移動量が減る調整ができゲーム機の性能が違っても同じ向く速度を実現できる。また、 / 100000としているのは調整値)。

 続いて「②距離をとって遠距離攻撃」を行う際の位置の指定方法だ。回り込む必要はなく、プレイヤーからLongAttackRange(以前記事で作ったステータス)の距離だけ離れている位置でしかもスケルトンから最も近い位置(つまりPlayerからスケルトンまでのベクトル上)を目標位置として指定する必要がある。
 これも厄介であり、再びQuaternionの出番となる。Quaternionは以下のように時計周りに角度が決まっている。

 Quaternion.Eulerを使えば以上のように角度と距離が分かれば位置を求めることができる。例えば以上の黄色の部分のように60度という角度と10という距離がわかっていれば(8.7,0,5)という位置を求めることができる。
 今回の場合Playerからスケルトンに伸びるベクトル上の長さが10(これがLongAttackRangeの値)の時の位置を求めて、NavMeshの目的地としたい。
 その場合以下の記述でOKだ。

 Quaternion.Eulerを使えば角度と距離が分かれば位置を求めることができる。
 角度を求めるための基準をVector3.forwardとした。kyorimukikeisanとの間をVector3.Angleで求める。ただQuaternionでは角度は以下のように時計周りとなっている。

 例えば以下のようにVector3.Angleで求めた角度がAであれば補正は必要ないが、Bの角度(Quaternionで180度以上となる角度)であれば補正が必要である。
 Quaternionの角度が180度以上かどうかはangle2ワールド座標の左方向のベクトルとkyorimukikeisanの間の角度を求め90度以下かどうか判断することで確認。Bの角度であれば360度-angleとすることでQuaternionで使える角度となる。
 後はNextPosition = Quaternion.Euler(0, angle, 0) * new Vector3(0, 0, Attackkyori);で目標位置を求める。ただ、Quaternionは原点からの位置を示す。よってPlayerの位置座標を加算することで目標位置を求められる。

 ③「Playerの左右に回り込んで近接攻撃」と④「Playerの左右に回り込んで遠距離攻撃」は②の応用。②と同様にして求めた角度angleに2分の1の確率で+90度、2分の1の確率で-90度を加算すればいい。③の場合は距離はShortAttackRangeで計算。

目次にもどる

実際のスクリプト記述

 参考までに、Enemystate1、Enemykoudou、Enemyidou1についてスクリプト全体の記述を示す(スケルトンにアタッチ)。

 Enemystate1(Enemykoudouのスクリプト呼び出しを一定間隔で行う)

 Enemykoudou(プレイヤーまでの距離から出す攻撃行動を制御)

 Enemyidou1(向きと移動関連のスクリプト)
 以上のように記述すれば4つの行動パターンを使い分けてくる敵AIを作ることができる。




 続きの記事は以下。
弾を撃つ
Unityのゲーム制作まとめへ戻る

page top