前提・実現したいこと
Unityを用いて2Dゲームを作っています。固定砲台の索敵範囲内に敵がいればその中からランダムに1体を選んでそちらを向いて発砲する、ということを実現したいと思っています。
該当のソースコード
まずオブジェクト同士の親子構造として、Turret.csがアタッチされたゲームオブジェクトTurretがあり、その子オブジェクトEnemySearchAreaには索敵用のスクリプトEnemySearchArea.csとサークルコライダーがアタッチされています。
索敵から攻撃までの流れは以下の通りです。
1、EnemySearchAreaのサークルコライダーに敵が接触したら、その敵のゲームオブジェクトをリストに追加
2、EnemySearchAreaのサークルコライダーから敵が出たら、その敵のゲームオブジェクトをリストから削除
3、リストの長さが0でない(=索敵範囲内に敵がいる)場合、その中からランダムに1体を選んで、そちらの方を向いて発砲
4、発砲するとゲームオブジェクトBulletが生成され、敵に当たるとダメージ処理を行う
以下がEnemySearchArea.csとなります。このスクリプトは索敵および索敵範囲内にいる敵リストの保持と、親オブジェクトであるTurretへの回転と発砲の指示を担当しています。
C#
1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class EnemySearchArea : MonoBehaviour 6{ 7 public bool setTarget; //ターゲットが設定されているか否か 8 private List<GameObject> targetList = new List<GameObject>(); //索敵範囲内にいるターゲットのリスト 9 10 // Start is called before the first frame update 11 void Start() 12 { 13 setTarget = false; 14 } 15 16 // Update is called once per frame 17 void Update() 18 { 19 if (!setTarget && targetList.Count > 0) //ターゲットがセットされておらず、かつ索敵範囲内に敵がいる場合 20 { 21 GameObject target = targetList[Random.Range(0, targetList.Count)]; //ターゲットをランダムに選択 22 setTarget = true; 23 this.transform.parent.GetComponent<Turret>().CoRotate(target); 24 this.transform.parent.GetComponent<Turret>().CoShot(); 25 } 26 } 27 28 private void OnTriggerEnter2D(Collider2D collision) 29 { 30 targetList.Add(collision.gameObject); 31 } 32 33 private void OnTriggerExit2D(Collider2D collision) 34 { 35 targetList.Remove(collision.gameObject); 36 } 37}
EnemySearchAreaからの指示を受けてTurret.csではゲームオブジェクトTurretの回転と発砲(ゲームオブジェクトBulletの生成)を行います。
C#
1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Turret : MonoBehaviour 6{ 7 public GameObject bullet; 8 public float coolTime; //発砲間隔 9 public int shotsNum; //連射数 10 public float turningSpeed; //旋回速度 11 12 IEnumerator Shot() //発砲コルーチン 13 { 14 for(int i = 0; i <= shotsNum; i++){ //0.1秒ごとに発射 15 Instantiate(bullet, transform.position, transform.rotation, transform); 16 yield return new WaitForSeconds(0.1f); 17 } 18 yield return new WaitForSeconds(coolTime); //連射後のクールタイム 19 this.transform.GetChild(1).GetComponent<EnemySearchRange>().setTarget = false; //ターゲットを未セットにしてコルーチン終了 20 yield break; 21 } 22 23 IEnumerator Rotate(GameObject target) //targetの方を向くまで一定速度で回転 24 { 25 while (Vector2.Angle(target.transform.position - this.transform.position, transform.up.normalized) > 5f) 26 { 27 this.GetComponent<Rigidbody2D>().angularVelocity = (Vector2.SignedAngle(transform.up.normalized, target.transform.position - this.transform.position) < 0 ? -1f : 1f) * turningSpeed; 28 yield return null; 29 } 30 this.GetComponent<Rigidbody2D>().angularVelocity = 0f; 31 yield break; 32 } 33 34 public void CoShot() //外部からShotコルーチンを呼ぶためのメソッド 35 { 36 StartCoroutine("Shot"); 37 } 38 39 public void CoRotate(GameObject target) //外部からRotateコルーチンを呼ぶためのメソッド 40 { 41 StartCoroutine(Rotate(target)); 42 } 43}
Turretから発射されたゲームオブジェクトBulletは敵と接触するとダメージ処理を行います。ダメージ処理の内容は敵にアタッチされたスクリプトCharacter.csのDamaged()メソッドに書かれています。
C#
1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Bullet : MonoBehaviour 6{ 7 public float speed; //弾速 8 public float damage; //1発当たりのダメージ量 9 10 private void OnTriggerEnter2D(Collider2D collision) 11 { 12 string layerName = LayerMask.LayerToName(collision.gameObject.layer); //衝突したオブジェクトのレイヤー名を取得 13 14 if (layerName == "Enemy" || layerName == "Player" ) 15 { 16 collision.GetComponent<Character>().Damaged(damage, GetComponent<Rigidbody2D>().velocity); 17 Destroy(this.gameObject); 18 } 19 } 20} 21
C#
1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Character : MonoBehaviour 6{ 7 public float maxHP; 8 private float HP; 9 10 11 public void Damaged(float damage, Vector2 dmgDirection) //ダメージ処理 12 { 13 this.HP = this.HP - damage; 14 15 if (this.HP < 0) 16 { 17 Destroy(gameObject); 18 } 19 20 this.GetComponent<Rigidbody2D>().AddForce(dmgDirection.normalized, ForceMode2D.Impulse); //ノックバック 21 22 } 23 24 // Start is called before the first frame update 25 void Start() 26 { 27 this.HP = maxHP; 28 } 29 30}
発生している問題・エラーメッセージ
実際に上記のコードを実行してみたところEnemySearchAreaの中のCoRotate()にtargetを渡すところで以下のようなエラーが発生しました。
MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object.
先ほどのEnemyearchArea.csでは索敵範囲内の敵がHP0になって消滅するパターンを考えていなかったため、既にDestroyされた敵をターゲットにしようとしてエラーが発生したようです。
教えていただきたいこと
それぞれのEnemySearchAreaクラスのインスタンスのtargetListからある敵のゲームオブジェクトを一斉に削除するような方法はないでしょうか?もしそのような方法があればCharacter.csのDamaged()メソッドのDestroyの直前に行うことで、上記の問題が解決するのではないかと思っています。
またもし別の解決方法(コードを根本的に変更する必要のあるものでも構いません)があればぜひとも教えていただけないでしょうか。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/12/29 15:35
2019/12/29 15:48
2019/12/30 07:27
2020/01/01 04:40