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

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

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

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

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

1351閲覧

unity2D 抽象クラス destory エラー

退会済みユーザー

退会済みユーザー

総合スコア0

C#

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

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2018/05/14 13:19

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public abstract class MovingObject : MonoBehaviour { 6 7 public float moveTime = 100f; 8 9 public LayerMask blockingLayer; 10 11 private BoxCollider2D boxCollider; 12 private Rigidbody2D rb2D; 13 //moveTimeを計算するのを単純化するための変数 14 private float inverseMoveTime; 15 16 // Use this for initialization 17 protected virtual void Start () { 18 //BoxCollider2DとRigidbody2Dを何度もGetComponentしなくて済むよう 19 //Startメソッドにてキャッシュしておく 20 boxCollider = GetComponent<BoxCollider2D>(); 21 rb2D = GetComponent<Rigidbody2D>(); 22 //デフォルトだと 1f ÷ 0.1f = 10.0f 23 inverseMoveTime = 1f / moveTime; 24 } 25 26 protected bool Move(int xDir, int yDir, out RaycastHit2D hit) 27 { 28 //現在地を取得 29 Vector2 start = transform.position; 30 //目的地を取得 31 Vector2 end = start + new Vector2(xDir, yDir); 32 //自身のColliderを無効にし、Linecastで自分自身を判定しないようにする 33 boxCollider.enabled = false; 34 //現在地と目的地との間にblockingLayerのついたオブジェクトが無いか判定 35 hit = Physics2D.Linecast(start, end, blockingLayer); 36 //Colliderを有効に戻す 37 boxCollider.enabled = true; 38 //何も無ければSmoothMovementへ遷移し移動処理 39 if (hit.transform == null) 40 { 41 StartCoroutine(SmoothMovement(end)); 42 //移動が成功したことを伝える 43 return true; 44 } 45 //移動に失敗したことを伝える 46 return false; 47 } 48 49 protected IEnumerator SmoothMovement(Vector3 end) 50 { 51 //現在地から目的地を引き、2点間の距離を求める(Vector3型) 52 //sqrMagnitudeはベクトルを2乗したあと2点間の距離に変換する(float型) 53 float sqrRemainingDistance = (transform.position - end).sqrMagnitude; 54 //2点間の距離が0になった時、ループを抜ける 55 //Epsilon : ほとんど0に近い数値を表す 56 while (sqrRemainingDistance > float.Epsilon) 57 { 58 //現在地と移動先の間を1秒間にinverseMoveTime分だけ移動する場合の、 59 //1フレーム分の移動距離を算出する 60 Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime); 61 //算出した移動距離分、移動する 62 rb2D.MovePosition(newPosition); 63 //現在地が目的地寄りになった結果、sqrRemainDistanceが小さくなる 64 sqrRemainingDistance = (transform.position - end).sqrMagnitude; 65 //1フレーム待ってから、while文の先頭へ戻る 66 yield return null; 67 } 68 } 69 70 71 //移動を試みるメソッド 72 //virtual : 継承されるメソッドに付ける修飾子 73 //<T>:ジェネリック機能 型を決めておかず、後から指定する 74 protected virtual void AttemptMove<T>(int xDir, int yDir) 75 //ジェネリック用の型引数をComponent型で限定 76 where T : Component 77 { 78 RaycastHit2D hit; 79 //Moveメソッド実行 戻り値がtrueなら移動成功、falseなら移動失敗 80 bool canMove = Move(xDir, yDir, out hit); 81 //Moveメソッドで確認した障害物が何も無ければメソッド終了 82 if (hit.transform == null) 83 { 84 return; 85 } 86 //障害物があった場合、障害物を型引数の型で取得 87 //型が<T>で指定したものと違う場合、取得できない 88 T hitComponent = hit.transform.GetComponent<T>(); 89 //障害物がある場合OnCantMoveを呼び出す 90 if (!canMove && hitComponent != null) 91 { 92 OnCantMove(hitComponent); 93 } 94 } 95 96 97 //abstract: メソッドの中身はこちらでは書かず、サブクラスにて書く 98 //<T>:AttemptMoveと同じくジェネリック機能 99 //障害物があり移動ができなかった場合に呼び出される 100 protected abstract void OnCantMove<T>(T component) where T : Component; 101} 102

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class Enemy : MovingObject 6{ 7 public Transform target; 8 private GameObject nearestfood; 9 private float dist; 10 private Statistics empty; 11 12 // Use this for initialization 13 protected override void Start() 14 { 15 GameManager.instance.AddEnemyToList(this); 16 17 GameObject[] foods = GameObject.FindGameObjectsWithTag("Food"); 18 foreach(GameObject food in foods) 19 { 20 Vector3 foodPos = food.transform.position; 21 dist = Vector3.Distance(this.transform.position, food.transform.position); 22 nearestfood = null; 23 if(dist < 16) 24 { 25 nearestfood = food; 26 target = nearestfood.transform; 27 } 28 } 29 30 //target = GameObject.FindGameObjectWithTag("Food").transform; 31 base.Start(); 32 33 empty = GetComponent<Statistics>(); 34 } 35 36 private void Update() 37 { 38 if(gameObject.transform.position == target.transform.position) 39 { 40 GameObject[] foods = GameObject.FindGameObjectsWithTag("Food"); 41 foreach (GameObject food in foods) 42 { 43 Vector3 foodPos = food.transform.position; 44 dist = Vector3.Distance(this.transform.position, food.transform.position); 45 nearestfood = null; 46 if (dist < 16) 47 { 48 nearestfood = food; 49 target = nearestfood.transform; 50 } 51 } 52 } 53 54 } 55 56 /*public void RemoveEnemy(int script) 57 { 58 if (empty.HP <= 0) 59 { 60 GameManager empty = GetComponent<GameManager>(); 61 empty.enemies.RemoveAt(script); 62 } 63 }*/ 64 65 protected override void AttemptMove<T>(int xDir, int yDir) 66 { 67 empty = GetComponent<Statistics>(); 68 empty.HP--; 69 //Call the AttemptMove function from MovingObject. 70 base.AttemptMove<T>(xDir, yDir); 71 } 72 73 //MoveEnemy is called by the GameManger each turn to tell each Enemy to try to move towards the player. 74 public void MoveEnemy() 75 { 76 //Declare variables for X and Y axis move directions, these range from -1 to 1. 77 //These values allow us to choose between the cardinal directions: up, down, left and right. 78 int xDir = 0; 79 int yDir = 0; 80 81 //If the difference in positions is approximately zero (Epsilon) do the following: 82 if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon) 83 84 //If the y coordinate of the target's (player) position is greater than the y coordinate of this enemy's position set y direction 1 (to move up). If not, set it to -1 (to move down). 85 yDir = target.position.y > transform.position.y ? 1 : -1; 86 87 88 //If the difference in positions is not approximately zero (Epsilon) do the following: 89 else 90 //Check if target x position is greater than enemy's x position, if so set x direction to 1 (move right), if not set to -1 (move left). 91 xDir = target.position.x > transform.position.x ? 1 : -1; 92 93 //Call the AttemptMove function and pass in the generic parameter Player, because Enemy is moving and expecting to potentially encounter a Player 94 AttemptMove<Food>(xDir, yDir); 95 96 } 97 98 private void OnTriggerEnter2D(Collider2D other) 99 { 100 if(other.tag == "Food") 101 { 102 other.gameObject.SetActive(false); 103 } 104 } 105 106 protected override void OnCantMove<T>(T component) 107 { 108 109 } 110} 111

C#

1#pragma warning disable 0414 2using System.Collections; 3using System.Collections.Generic; 4using UnityEngine; 5using UnityEngine.UI; 6 7public class Statistics : MonoBehaviour 8{ 9 Text targetText; 10 11 int Level;//レベル 12 string Ability;//特性 13 public int HP; 14 int Attack;//こうげき 15 int Defense;//ぼうぎょ 16 int Sp_Atk;//とくこう 17 int Sp_Def;//とくぼう 18 public int Speed;//すばやさ 19 int Evasion;//回避 20 int Accuracy;//命中 21 string Gender;//性別 22 int Exp;//経験値 23 int Individual_Value;//個体値 24 string ATKorSPATK; 25 26 int OK = 0; 27 bool OK_Click = true; 28 29 Text SpeciesText; 30 string Text; 31 Image targetImage; 32 33 GameObject targetObj; 34 35 // ラジアン変数 36 private float rad; 37 // 現在位置を代入する為の変数 38 private Vector2 Position; 39 40 public Vector2 speed = new Vector2(0.01f, 0.01f); 41 42 private void Awake() 43 { 44 SpeciesText = GameObject.Find("SpeciesText").GetComponent<Text>(); 45 Text = "Species:"; 46 SpeciesText.text = Text; 47 targetImage = GameObject.Find("SpeciesImage").GetComponent<Image>(); 48 targetText = GameObject.Find("StatsText").GetComponent<Text>(); 49 Generate_Stats(); 50 targetText.enabled = false; 51 } 52 53 private void Update() 54 { 55 targetText.text = "Level " + Level.ToString() + "\n" + "Ability " + Ability + "\n" + "Gender " + Gender + "\n" + "IV " + Individual_Value.ToString() + "\n" + 56 "HP " + HP.ToString() + "\n" + "Attack " + Attack.ToString() + "\n" + "Defense " + Defense.ToString() + "\n" + 57 "Sp_Atk " + Sp_Atk.ToString() + "\n" + "Sp_Def " + Sp_Def.ToString() + "\n" + "Speed " + Speed.ToString() + "\n" + 58 "Evasion " + Evasion.ToString() + "\n" + "Accuracy " + Accuracy.ToString() + "\n" + "Exp " + Exp.ToString() + "\n" + "ATK Type " + ATKorSPATK; 59 60 if (HP <= 0) 61 { 62 Enemy enemy = GetComponent<Enemy>(); 63 enemy.enabled = false; 64 GameObject obj = this.gameObject; 65 GameObject.Destroy(obj); 66 obj = null; 67 } 68 } 69//省略

Unity2dのローグライクチュートリアルを改造しているのですが、StatisticsスクリプトのUpdateで(HP <= 0)になったとき、EnemyスクリプトとStatisticsスクリプトがアタッチされているオブジェクトをdestoryするようにしました。

ですが、destoryすると、Enemyスクリプトの

if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon)

という部分で

MissingReferenceException: The object of type 'Enemy' 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.

というエラーが出て来てしまいます。

抽象クラスをいまいち理解出来ていないので、どうやればdestoryしたときに、エラーを回避出来るかが分かりませんでした。

出来ればエラーを回避する方法を教えていただきたいです。
回答お願いします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.SceneManagement; 5 6public class Enemy : MovingObject 7{ 8 public Transform target; 9 private GameObject nearestfood; 10 private float dist; 11 private Statistics empty; 12 13 // Use this for initialization 14 protected override void Start() 15 { 16 GameManager.instance.AddEnemyToList(this); 17 18 GameObject[] foods = GameObject.FindGameObjectsWithTag("Food"); 19 foreach(GameObject food in foods) 20 { 21 Vector3 foodPos = food.transform.position; 22 dist = Vector3.Distance(this.transform.position, food.transform.position); 23 nearestfood = null; 24 if(dist < 16) 25 { 26 nearestfood = food; 27 target = nearestfood.transform; 28 } 29 } 30 31 //target = GameObject.FindGameObjectWithTag("Food").transform; 32 base.Start(); 33 34 empty = GetComponent<Statistics>(); 35 } 36 37 private void Update() 38 { 39 if(gameObject.transform.position == target.transform.position) 40 { 41 GameObject[] foods = GameObject.FindGameObjectsWithTag("Food"); 42 foreach (GameObject food in foods) 43 { 44 Vector3 foodPos = food.transform.position; 45 dist = Vector3.Distance(this.transform.position, food.transform.position); 46 nearestfood = null; 47 if (dist < 16) 48 { 49 nearestfood = food; 50 target = nearestfood.transform; 51 } 52 } 53 } 54 55 if(empty.HP <= 0) 56 { 57 GameManager.instance.RemoveEnemyToList(this); 58 Destroy(gameObject); 59 } 60 } 61 62 /*public void RemoveEnemy(int script) 63 { 64 if (empty.HP <= 0) 65 { 66 GameManager empty = GetComponent<GameManager>(); 67 empty.enemies.RemoveAt(script); 68 } 69 }*/ 70 71 protected override void AttemptMove<T>(int xDir, int yDir) 72 { 73 empty = GetComponent<Statistics>(); 74 empty.HP--; 75 //Call the AttemptMove function from MovingObject. 76 base.AttemptMove<T>(xDir, yDir); 77 } 78 79 //MoveEnemy is called by the GameManger each turn to tell each Enemy to try to move towards the player. 80 public void MoveEnemy() 81 { 82 //Declare variables for X and Y axis move directions, these range from -1 to 1. 83 //These values allow us to choose between the cardinal directions: up, down, left and right. 84 int xDir = 0; 85 int yDir = 0; 86 87 //If the difference in positions is approximately zero (Epsilon) do the following: 88 if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon) 89 90 //If the y coordinate of the target's (player) position is greater than the y coordinate of this enemy's position set y direction 1 (to move up). If not, set it to -1 (to move down). 91 yDir = target.position.y > transform.position.y ? 1 : -1; 92 93 94 //If the difference in positions is not approximately zero (Epsilon) do the following: 95 else 96 //Check if target x position is greater than enemy's x position, if so set x direction to 1 (move right), if not set to -1 (move left). 97 xDir = target.position.x > transform.position.x ? 1 : -1; 98 99 //Call the AttemptMove function and pass in the generic parameter Player, because Enemy is moving and expecting to potentially encounter a Player 100 AttemptMove<Food>(xDir, yDir); 101 102 } 103 104 private void OnTriggerEnter2D(Collider2D other) 105 { 106 if(other.tag == "Food") 107 { 108 other.gameObject.SetActive(false); 109 } 110 } 111 112 protected override void OnCantMove<T>(T component) 113 { 114 115 } 116 117} 118

Enemyスクリプトが加わっているListにアクセスして、そこからEnemyがアタッチされているGameObjectのHPが0になったとき、Enemy自身を引数として渡して、要素を削除してから、GameObjectをDestoryしたら、エラーになりませんでした。

投稿2018/05/15 05:45

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

コードを全部読んだわけではありません。コメントだけ。

エラーメッセージは、EnemyタイプのオブジェクトがすでにDestroyされているのに、それにアクセスしようとした、というものです。
このメッセージは対象が抽象クラスかどうかに関係ありません。

対象のオブジェクトがDestroyされたあとに、そのオブジェクトが持つ変数やメソッドを見に行っている箇所がないか確認してください。

なお(エラーの解消には直接関係ありませんが)、OnDestroyメソッドを使うと、Destoryされる際に一緒に実行する処理を書くことができるので、そういうものも活用すると処理を整理しやすいかもしれません。

投稿2018/05/14 16:36

negitama

総合スコア943

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/05/14 16:55

回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問