Unity 3D入門 #15 [敵の配置]

NavMeshによってプレイヤーを追いかける敵の設定
参考URL：https://tech.pjin.jp/blog/2020/04/30/unity_navmesh_startup/
ドアを通る参考URL：https://gametukurikata.com/navigation/navmovearea

また、ドアを通るときはドアを開け、通った後にドアを閉めるようにします。

```
Ray ray1 = new Ray(this.transform.position, -this.transform.forward);
RaycastHit hit1;
if (Physics.Raycast(ray1, out hit1, maxDist){
    // 通ったドアの鍵を閉める
    if (hit1.collider.gameObject.tag == "Door"){
       if(!hit1.collider.gameObject.GetComponent<DoorOpen>().IsLocked){
           if(!hit1.collider.gameObject.GetComponent<DoorOpen>().DoorClosed){
               //ドアが開いている場合、ドアを閉じる
               hit1.collider.gameObject.GetComponent<DoorOpen>().Activate();
           }
           // 鍵が開いている場合、鍵を閉める
           hit1.collider.gameObject.GetComponent<DoorOpen>().IsLocked = true;
       }
    }
}

Ray ray2 = new Ray(this.transform.position, this.transform.forward);
RaycastHit hit2;
if (Physics.Raycast(ray2, out hit2, maxDist)){
    // 通ったドアの鍵を開ける
    if (hit2.collider.gameObject.tag == "Door"){
        // 鍵がしまっている場合、鍵を開ける
        hit2.collider.gameObject.GetComponent<DoorOpen>().IsLocked = false;
        //ドアが閉じている場合、ドアを開ける
        if(hit2.collider.gameObject.GetComponent<DoorOpen>().DoorClosed){
            hit2.collider.gameObject.GetComponent<DoorOpen>().Activate();
        }
    }
}
```

上のコードにおいて、ドアオブジェクトはDoorOpen.csをアタッチされており、isLockedとisOpenの二つの変数に今のドアの状態を記録している。
isLocked -> true:鍵がかかっている, false:鍵が開いている
isOpen -> true:ドアが開いている, false:ドアが閉まっている

次に、敵がプレイヤーを発見するスクリプトを書きます。

```
// 半径3以内に近づいたら発見
if(Vector3.Distance(this.transform.position, goal.transform.position) < 3){
    isFind = true;
    isLast = false;
}
// 間に壁がなければ発見
RaycastHit hit;
// ターゲットオブジェクトとの差分を求め
Vector3 temp =  goal.transform.position - this.transform.position;
// 正規化して方向ベクトルを求める
Vector3 normal = temp.normalized;
if (Physics.Raycast (this.transform.position, normal, out hit, maxrange)) {
    if (hit.transform.gameObject == goal) {
        isFind = true;
        isLast = false;
    }else{
        isFind = false;
    }
}
```

二つの条件「半径3以内に接近」「プレイヤーとの間に壁がなく直線距離がmaxrange以内」の場合に敵はプレイヤーを発見します。
発見した場合、isFindをtrueにし、見失った場合isFindをfalseにします。
isLastは後で使います。

また、プレイヤーを発見した場合はプレイヤーを追いかけますが、プレイヤーを見失った場合も最後にプレイヤーを見た場所まで移動してもらいます。

```
if(isFind){
    agent.destination = goal.transform.position;
}else{
    agent.destination = goal.transform.position;
    // プレイヤーを最後に発見した場所に行く
    if(!isLast){
        if(Vector3.Distance(this.transform.position, agent.destination) < 3){
            isLast = true;
        }
    }else{
        // 徘徊
    }
}
```

こう書くことでプレイヤーを見失った地点まで移動するとisLastがtrueになり、「// 徘徊」行動に移ります。

徘徊行動は https://getabakoclub.com/2019/01/09/unityの便利機能を使いこなそう-4-navmeshagentで敵のaiをつくろ/ を参考に実装しました。

```
public Vector3[] wayPoints = new Vector3[3];//徘徊するポイントの座標を代入するVector3型の変数を配列で作る
private int currentRoot = 0;//現在目指すポイントを代入する変数

void Start () {	
    agent.destination = wayPoints[currentRoot]; // 初期目的地
}

void Update () {	
    if (Vector3.Distance (this.transform.position, wayPoints[currentRoot]) < 1f) {//到着した場合
        Invoke("updateDestination", 3f);
    }
}

void updateDestination(){
    currentRoot = Mathf.FloorToInt(Random.Range (0, wayPoints.Length));
    agent.destination = wayPoints[currentRoot];
}
```

配列wayPointsに徘徊先目的地を格納し、到着した場合はupdateDestination関数を呼び、目的地を更新する。もちろん目的地までの動きはNavMeshを使用している。

また、敵がどこかに詰まってしまった場合に備えて、同じ場所に5秒以上留まっていたら目的地の更新を行うプログラムを用意する。

```
private float process_time = 0;
private Vector3 lastPosition;

void Start () {	
    lastPosition = this.transform.position;
}

void Update () {
    // 引っかかったとき、徘徊に戻らせる
    if(lastPosition == this.transform.position){
        process_time += Time.deltaTime;
        if(process_time > 5){
            updateDestination();
            process_time = 0;
        }
    }else{
        process_time = 0;
        lastPosition = this.transform.position;
    }
}
```

使用しているupdateDestination関数はさらに一つ上のコードに記載してある。

これで大まかに敵AIの設計が終わった。