通行人AIを自作するためのヒント
- BTA(Kelorin Jo)
- 2020年12月9日
- 読了時間: 6分
更新日:2020年12月29日
(20/12/12 誤字が多かったので修正しました)
単にウェイポイントを巡回(パトロール)するだけでなく、路上で興味を引くものに反応する通行人AIを実装しました。
道のガードレールに座り、テーブルについて食事を食べ、ショーウィンドーを眺め、広場の壁際により掛かります。
NPCが興味を引くもの-以降「インタレスト」と呼称-をNPCに認識させるのに、巡回AIのウェイポイントのように固定ではなくランダムにシーンに配置できて、難しい設定を必要とせずしっかり反応する、というものをめざしましたが。
それには
・まず前提として、視界を持ってウェイポイントを巡回できるAIコンポーネント+ナビメッシュエージェントがアタッチされてる前提になります。
・キャラには、「手をふる」とか「椅子に座る」などのモーション(のステート名)と、そのモーションを移動せずやり続ける「待機時間」、キャラの位置を固定するオプション(Bool)、そして見物する・スマホで撮るなどの同種のアクションをある程度選別してランダムに行うためのカテゴリ分けした「アクションID」、のリストを持つコンポーネントを作ってアタッチします。
こんな感じ↓

・インタレスト側は、コライダーとタグをつけてレイキャストなどに反応できるようにして、アクションID、キャラの座標指定、手に持たせるオブジェクト(とそのオフセット座標)などを設定したコンポーネントを作ってアタッチします。
こんな感じ↓

・AIのレイキャスト視界機能で探りながら巡回するAIを組んであるキャラに、インタレストが視界に入ったら巡回をやめてインタレストに近づき、最接近したらこの自作スクリプトのメソッドを実行、直後にAIとナビメッシュを一時停止、という挙動を追加。
メソッドが実行されるとインタレストから情報を取得し、インタレストの指定する座標にキャラを移動+回転(というかワープ)させて、IDで指定したモーションを一定時間とりつづける処理を行います。
タイマー実行には、このアセットを使いました。
紹介記事
安直に使えて超便利なやつ。
AIの動作と競合しないようだし、PuppetMasterとかも効いたままにできます。
(追記:今だったらUniTaskのほうがいいんだろうか。)


※ちなみにウェイポイントやインタレストが透明だと座標合わせがやりづらいので、マーカーを自作してアタッチしてます。

・モーションは、アニメーターのパラメータで指定するとトランジョンの数とパラメータの書き換えの手間がかなりのコストになるので、パラメータ無しで直接ステートを変更するやり方をしてます。
参考記事
処理が終わったらIdleか歩行のステートに戻るようにします。
(ただしこの方法はコンソールに警告が山ほど出てしまう)

・椅子に座るなどの一部のアクションはキャラを特定の位置に固定させる必要がありますが、
ナビメッシュを無効化しないと、ナビメッシュの外にある椅子やガードレールの上に移動させることができません。動かしてもナビメッシュの境界まで戻されます。またキャラのRigidbodyも無効にするか、isKinematicをオンにします。今回は無効だとPuppetMasterがエラーになるので、isKinematicをオンです。
・モーションし終わったら、AIとナビメッシュを再開して、巡回行動に戻します
AIも無効にする前にナビメッシュを無効にすると、AIはナビメッシュに依存してるから赤エラーが出てしまいますので、AI-ナビメッシュの順で無効化して、再開する時は逆にナビメッシュ-AIの順で有効化します。
あとRigidbodyも戻します。

・インタレストが密集してたら2-3箇所の場所で行動がループしてしまうので、一度アクションしたら一定時間アクションしない、もしくはインタレストが自らのコライダーを一体時間無効にする「クールダウンタイム」も設定します。
・アクションの途中で攻撃されるとか中断することがあったら、即座にAIとナビメッシュを再開するようにします。この作例ではキャラがダウンするのはPuppetMasterの管轄なので、そちらのコンポーネントのダウン時イベントでも、同様にナビメッシュとAIの無効化・有効化を設定する必要があります。
・NPCが複数いると同時に同じインタレストに反応する場合があり、椅子に座るなどで同じ座標に一複数座られては困るから、すでに誰か先に座ってる場合はアクションに移る前に中断して巡回に戻れるようにする必要があります。(例えば先に行動したキャラがインタレストのコライダーを無効化すれば、AIは見失って巡回に戻るでしょう)
野次馬など、位置のロックは必要なく大まかに近寄ればいいアクションの場合は、複数のキャラが同一のインタレストに反応してもいい設定にします。ただし、アクションが終わった後キャラが渋滞する問題が出ます。
・位置のロックは、キャラが短距離ワープするのでやや不自然な見た目になります。インタレストになるべく接近させてからLerpとRotationと遅延実行で、するーっと移動+回転してスムーズにアクションに移行できたらいいのですが、コーディングが難しくなります(つまり実装してない)。
・食事など手にものを持たせてアクションさせたい場合は、インタレスト側でオブジェクトを指定してシーンに配置、もしくは見えないところに隠しておき、アクション時にキャラの手ボーンなどの位置にワープもしくはインスタンスを生成させて、中断したら削除することで実現できます。手以外のボーンに指定したい場合はまあ、ボーンのオブジェクト名とfindなどで

長いこと動かすとインタレストに反応しなくなっていく不具合が潜んでるっぽいですが、NPCが殴られてダウンしたり、プレイヤーから長時間離れると消えて別人としてリスポンするコンポーネントもつけたらリセットされるようなので、放置してます。
そんな感じの仕様で、長くなったものの思ったより難しいことせずに実装できましたが、欲張れば特定のキャラに反応して挨拶して親密度が増減したり、時間で変動して一日の生活挙動を実現したりもできそうです。
まあ今回はそこまではやらないですが
作ってみてわかったことが、これ巡回のウェイポイントを「アクションを特に指定してないインタレスト」に置き換えたら、AIのアセットはいらないのではとなりました。
ナビメッシュは必要だけど、視界の判定が自作できれば もしくは判定をコライダーでなくキャラとインタレストの距離を(一定時間ごとに)判定すれば済みそうで、
AIアセットのウェイポイントはNPCをスポーンしたとき・別シーンで使い回すのにいちいち設定し直すのが面倒だし、シーンに適当に配置したらキャラが勝手に誘導されていい感じにランダムで動いてくれるインタレストシステムだけでいいんじゃないのと。
それと、AIアセット…Behavior Designerがなんか同じAIを実装したNPCを複製で増やしたら無限ループのフリーズを起こす不具合があるのがわかりました。
複製したNPCのAIがそれぞれ固有で持つべき変数の一部をなぜか全キャラが共有してしまって、一斉に同一インタレストに向かったり全然向かわなくなったりなど怪しい動きをしてたりしたので、面倒な見えない問題を抱えてるのではと。
でも集団行動は普通にできるはずなんだけど、このAI…
アセットに関して情報を探っても見つからずで、だったらもうアセットの使えるコードだけ真似して簡易AIを自作して置き換えるかなあと思ってきたりです。
Comments