質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
87.20%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

解決済

【Unity】自動で迎撃する固定砲台の処理について

shukrin
shukrin

総合スコア14

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

1回答

0評価

1クリップ

558閲覧

投稿2019/12/29 13:51

前提・実現したいこと

Unityを用いて2Dゲームを作っています。固定砲台の索敵範囲内に敵がいればその中からランダムに1体を選んでそちらを向いて発砲する、ということを実現したいと思っています。

該当のソースコード

まずオブジェクト同士の親子構造として、Turret.csがアタッチされたゲームオブジェクトTurretがあり、その子オブジェクトEnemySearchAreaには索敵用のスクリプトEnemySearchArea.csとサークルコライダーがアタッチされています。

索敵から攻撃までの流れは以下の通りです。
1、EnemySearchAreaのサークルコライダーに敵が接触したら、その敵のゲームオブジェクトをリストに追加
2、EnemySearchAreaのサークルコライダーから敵が出たら、その敵のゲームオブジェクトをリストから削除
3、リストの長さが0でない(=索敵範囲内に敵がいる)場合、その中からランダムに1体を選んで、そちらの方を向いて発砲
4、発砲するとゲームオブジェクトBulletが生成され、敵に当たるとダメージ処理を行う

以下がEnemySearchArea.csとなります。このスクリプトは索敵および索敵範囲内にいる敵リストの保持と、親オブジェクトであるTurretへの回転と発砲の指示を担当しています。

C#

using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemySearchArea : MonoBehaviour { public bool setTarget; //ターゲットが設定されているか否か private List<GameObject> targetList = new List<GameObject>(); //索敵範囲内にいるターゲットのリスト // Start is called before the first frame update void Start() { setTarget = false; } // Update is called once per frame void Update() { if (!setTarget && targetList.Count > 0) //ターゲットがセットされておらず、かつ索敵範囲内に敵がいる場合 { GameObject target = targetList[Random.Range(0, targetList.Count)]; //ターゲットをランダムに選択 setTarget = true; this.transform.parent.GetComponent<Turret>().CoRotate(target); this.transform.parent.GetComponent<Turret>().CoShot(); } } private void OnTriggerEnter2D(Collider2D collision) { targetList.Add(collision.gameObject); } private void OnTriggerExit2D(Collider2D collision) { targetList.Remove(collision.gameObject); } }

EnemySearchAreaからの指示を受けてTurret.csではゲームオブジェクトTurretの回転と発砲(ゲームオブジェクトBulletの生成)を行います。

C#

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Turret : MonoBehaviour { public GameObject bullet; public float coolTime; //発砲間隔 public int shotsNum; //連射数 public float turningSpeed; //旋回速度 IEnumerator Shot() //発砲コルーチン { for(int i = 0; i <= shotsNum; i++){ //0.1秒ごとに発射 Instantiate(bullet, transform.position, transform.rotation, transform); yield return new WaitForSeconds(0.1f); } yield return new WaitForSeconds(coolTime); //連射後のクールタイム this.transform.GetChild(1).GetComponent<EnemySearchRange>().setTarget = false; //ターゲットを未セットにしてコルーチン終了 yield break; } IEnumerator Rotate(GameObject target) //targetの方を向くまで一定速度で回転 { while (Vector2.Angle(target.transform.position - this.transform.position, transform.up.normalized) > 5f) { this.GetComponent<Rigidbody2D>().angularVelocity = (Vector2.SignedAngle(transform.up.normalized, target.transform.position - this.transform.position) < 0 ? -1f : 1f) * turningSpeed; yield return null; } this.GetComponent<Rigidbody2D>().angularVelocity = 0f; yield break; } public void CoShot() //外部からShotコルーチンを呼ぶためのメソッド { StartCoroutine("Shot"); } public void CoRotate(GameObject target) //外部からRotateコルーチンを呼ぶためのメソッド { StartCoroutine(Rotate(target)); } }

Turretから発射されたゲームオブジェクトBulletは敵と接触するとダメージ処理を行います。ダメージ処理の内容は敵にアタッチされたスクリプトCharacter.csのDamaged()メソッドに書かれています。

C#

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Bullet : MonoBehaviour { public float speed; //弾速 public float damage; //1発当たりのダメージ量 private void OnTriggerEnter2D(Collider2D collision) { string layerName = LayerMask.LayerToName(collision.gameObject.layer); //衝突したオブジェクトのレイヤー名を取得 if (layerName == "Enemy" || layerName == "Player" ) { collision.GetComponent<Character>().Damaged(damage, GetComponent<Rigidbody2D>().velocity); Destroy(this.gameObject); } } }

C#

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Character : MonoBehaviour { public float maxHP; private float HP; public void Damaged(float damage, Vector2 dmgDirection) //ダメージ処理 { this.HP = this.HP - damage; if (this.HP < 0) { Destroy(gameObject); } this.GetComponent<Rigidbody2D>().AddForce(dmgDirection.normalized, ForceMode2D.Impulse); //ノックバック } // Start is called before the first frame update void Start() { this.HP = maxHP; } }

発生している問題・エラーメッセージ

実際に上記のコードを実行してみたところ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の直前に行うことで、上記の問題が解決するのではないかと思っています。
またもし別の解決方法(コードを根本的に変更する必要のあるものでも構いません)があればぜひとも教えていただけないでしょうか。

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

まだ回答がついていません

会員登録して回答してみよう

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
87.20%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。