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の設計が終わった。