今回はUnityで敵が見回りをしている最中に敵のサーチエリア内に主人公が侵入しても敵の視覚に入っていない場合は主人公を発見出来ないようにしていきたいと思います。
敵のサーチエリア内に入ったら主人公を追いかける機能は
で作成した敵の機能を使います。
今回の機能を作成すると、
上のような感じになります。
敵のサーチエリア(コライダ)内に主人公が侵入していますが敵の視覚内に入っていない時は主人公を発見出来ません。
サーチエリア内の敵の視覚に入ったら敵が主人公を追いかけてくるようになります。
今回作成する機能は先ほどの記事で作成したものをちょこっと変更するだけなのですぐに出来ます。
敵の視覚を作成する
敵の子要素にCreate Emptyで空のゲームオブジェクトを作成し名前をSearchAreaとします。
SearchAreaを選択しAdd Component→Physics→Sphere Colliderを取り付け、コライダのサイズを調整します。
主人公をサーチするのはコライダで行います。
実際に作成したコライダは
上のようになりました。
主人公をサーチするスクリプトの変更
SearchAreaゲームオブジェクトには主人公をサーチするスクリプトを取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; public class OnlyForwardSearch : MonoBehaviour { [SerializeField] private OnlyForwardSearchEnemy onlyForwardSearchEnemy; [SerializeField] private SphereCollider searchArea; [SerializeField] private float searchAngle = 130f; private void OnTriggerStay(Collider other) { if(other.tag == "Player") { // 主人公の方向 var playerDirection = other.transform.position - transform.position; // 敵の前方からの主人公の方向 var angle = Vector3.Angle(transform.forward, playerDirection); // サーチする角度内だったら発見 if (angle <= searchAngle) { Debug.Log("主人公発見: " + angle); onlyForwardSearchEnemy.SetState(OnlyForwardSearchEnemy.EnemyState.Chase, other.transform); } } } private void OnTriggerExit(Collider other) { if(other.tag == "Player") { onlyForwardSearchEnemy.SetState(OnlyForwardSearchEnemy.EnemyState.Wait); } } #if UNITY_EDITOR // サーチする角度表示 private void OnDrawGizmos() { Handles.color = Color.red; Handles.DrawSolidArc(transform.position, Vector3.up, Quaternion.Euler(0f, -searchAngle, 0f) * transform.forward, searchAngle * 2f, searchArea.radius); } #endif } |
インスペクタで敵の操作スクリプトであるonlyForwardSearchEnemy、主人公を検知するコライダ、敵の視覚の角度を指定出来るようにします。
敵の操作スクリプトがOnlyForwardSearchEnemyとなっていますが、これはわたくしの都合上そうなっているだけなので気にしないでください。
OnTriggerStayメソッドはSearchAreaのコライダに他のコライダが入ってきた時に呼ばれます。
ここでOnTriggerEnterにしていないのは主人公がコライダ内に入っても視覚内にいない場合は敵から発見されませんが、視覚内にコライダから出ずに入った時も発見されるようにする為です。
OnTriggerEnterはコライダに侵入した時しか呼ばれないのでコライダ内で視覚外から視覚内と移動した時の判定をする為にOnTriggerStayを使用しています。
主人公のゲームオブジェクトにはPlayerタグを設定しておき、侵入したコライダのゲームオブジェクトがPlayerタグだった時に処理をしていきます。
otherが侵入したコライダなので、そのコライダの位置(主人公の位置)から自身の位置(敵の位置)を引いて敵からみた主人公の方向ベクトルを計算します。
Vector3.Angleを使用して敵の前方と主人公の方向の角度を計算します。
Vector3.Angleは0~180度の数値が返ってきます。
計算した角度が指定した角度内だった時に主人公を発見したということにしています。
これで主人公が敵のサーチエリアに侵入した時で敵の視覚内の時だけ発見したことに出来ます。
OnTriggerExitはサーチエリアのコライダから他のコライダが出ていった時に呼び出されるので、そこでPlayerタグを持つコライダだったら敵を待ち状態に変更します。
OnDrawGizmosはシーンビューにギズモを表示するメソッドで、マイフレーム呼ばれます。
Handles.DrawSolidArcで敵の視覚を扇で表示しています。
DrawSolidArcの第1引数は扇の中心位置、第2引数は表示する表面の方向、第3引数は扇の表示を開始する方向、第4引数は扇の角度、第5引数は扇の半径を指定します。
第3引数で行っているのは扇を開始する方向の調整で視覚の角度に応じて表示する方向を変更しています。
ただ単にtransform.forwardとすると敵の前方から扇が表示されるので、敵の前方の左右に均等に扇が表示されるよう表示される方向を前方のマイナス方向にずらしています。
第4引数でsearchAngleを2倍しているのは左右にsearchAngle分の扇を表示する為です。
第5引数ではサーチエリアのコライダの半径を渡してサーチエリアのコライダとサイズを合わせています。
OnlyForwardSearchスクリプトのインスペクタでコライダなどを設定しないとコンソールにエラーが表示されるので気を付けてください。
敵の視覚外でも距離に応じて発見する
敵の視覚内に侵入した時だけ主人公を発見出来るようにしましたが、視覚外でも角度によって発見する距離を変えるというのも出来そうですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | private void OnTriggerStay(Collider other) { if (other.tag == "Player") { var playerDirection = other.transform.position - transform.position; var angle = Vector3.Angle(transform.forward, playerDirection); if (angle <= searchAngle) { Debug.Log("主人公を発見1"); } else { if (Vector3.Distance(other.transform.position, transform.position) <= searchArea.radius * 0.5f) { Debug.Log("主人公を発見2"); } } } } |
上では視覚外の時は主人公と敵の距離がサーチエリアのコライダの半径の半分以下だった時は発見するようにしています。
終わりに
コライダだけで侵入を検知する事も出来ますが、視覚内や距離に応じて検知する方がよりリアルな感じに出来ますね。
ダークソウルのバックスタブのような機能を作りたい時は敵の視覚を作っておくと敵に発見されずに後ろから近づけそうですね。
主人公の移動速度も考慮する必要があるかも。