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

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

ただいまの
回答率

90.50%

  • C#

    7119questions

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

  • Unity

    4004questions

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

  • Unity2D

    884questions

unity2D 抽象クラス destory エラー

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 137

t2t21212

score 29

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class MovingObject : MonoBehaviour {

    public float moveTime = 100f;

    public LayerMask blockingLayer;

    private BoxCollider2D boxCollider;
    private Rigidbody2D rb2D;
    //moveTimeを計算するのを単純化するための変数
    private float inverseMoveTime;

    // Use this for initialization
    protected virtual void Start () {
        //BoxCollider2DとRigidbody2Dを何度もGetComponentしなくて済むよう
        //Startメソッドにてキャッシュしておく
        boxCollider = GetComponent<BoxCollider2D>();
        rb2D = GetComponent<Rigidbody2D>();
        //デフォルトだと 1f ÷ 0.1f = 10.0f
        inverseMoveTime = 1f / moveTime;
    }

    protected bool Move(int xDir, int yDir, out RaycastHit2D hit)
    {
        //現在地を取得
        Vector2 start = transform.position;
        //目的地を取得
        Vector2 end = start + new Vector2(xDir, yDir);
        //自身のColliderを無効にし、Linecastで自分自身を判定しないようにする
        boxCollider.enabled = false;
        //現在地と目的地との間にblockingLayerのついたオブジェクトが無いか判定
        hit = Physics2D.Linecast(start, end, blockingLayer);
        //Colliderを有効に戻す
        boxCollider.enabled = true;
        //何も無ければSmoothMovementへ遷移し移動処理
        if (hit.transform == null)
        {
            StartCoroutine(SmoothMovement(end));
            //移動が成功したことを伝える
            return true;
        }
        //移動に失敗したことを伝える
        return false;
    }

    protected IEnumerator SmoothMovement(Vector3 end)
    {
        //現在地から目的地を引き、2点間の距離を求める(Vector3型)
        //sqrMagnitudeはベクトルを2乗したあと2点間の距離に変換する(float型)
        float sqrRemainingDistance = (transform.position - end).sqrMagnitude;
        //2点間の距離が0になった時、ループを抜ける
        //Epsilon : ほとんど0に近い数値を表す
        while (sqrRemainingDistance > float.Epsilon)
        {
            //現在地と移動先の間を1秒間にinverseMoveTime分だけ移動する場合の、
            //1フレーム分の移動距離を算出する
            Vector3 newPosition = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);
            //算出した移動距離分、移動する
            rb2D.MovePosition(newPosition);
            //現在地が目的地寄りになった結果、sqrRemainDistanceが小さくなる
            sqrRemainingDistance = (transform.position - end).sqrMagnitude;
            //1フレーム待ってから、while文の先頭へ戻る
            yield return null;
        }
    }


    //移動を試みるメソッド
    //virtual : 継承されるメソッドに付ける修飾子
    //<T>:ジェネリック機能 型を決めておかず、後から指定する
    protected virtual void AttemptMove<T>(int xDir, int yDir)
        //ジェネリック用の型引数をComponent型で限定
        where T : Component
    {
        RaycastHit2D hit;
        //Moveメソッド実行 戻り値がtrueなら移動成功、falseなら移動失敗
        bool canMove = Move(xDir, yDir, out hit);
        //Moveメソッドで確認した障害物が何も無ければメソッド終了
        if (hit.transform == null)
        {
            return;
        }
        //障害物があった場合、障害物を型引数の型で取得
        //型が<T>で指定したものと違う場合、取得できない
        T hitComponent = hit.transform.GetComponent<T>();
        //障害物がある場合OnCantMoveを呼び出す
        if (!canMove && hitComponent != null)
        {
            OnCantMove(hitComponent);
        }
    }


    //abstract: メソッドの中身はこちらでは書かず、サブクラスにて書く
    //<T>:AttemptMoveと同じくジェネリック機能
    //障害物があり移動ができなかった場合に呼び出される
    protected abstract void OnCantMove<T>(T component) where T : Component;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MovingObject
{
    public Transform target;
    private GameObject nearestfood;
    private float dist;
    private Statistics empty;

    // Use this for initialization
    protected override void Start()
    {
        GameManager.instance.AddEnemyToList(this);

        GameObject[] foods = GameObject.FindGameObjectsWithTag("Food");
        foreach(GameObject food in foods)
        {
            Vector3 foodPos = food.transform.position;
            dist = Vector3.Distance(this.transform.position, food.transform.position);
            nearestfood = null;
            if(dist < 16)
            {
                nearestfood = food;
                target = nearestfood.transform;
            }
        }

        //target = GameObject.FindGameObjectWithTag("Food").transform;
        base.Start();

        empty = GetComponent<Statistics>();
    }

    private void Update()
    {
        if(gameObject.transform.position == target.transform.position)
        {
            GameObject[] foods = GameObject.FindGameObjectsWithTag("Food");
            foreach (GameObject food in foods)
            {
                Vector3 foodPos = food.transform.position;
                dist = Vector3.Distance(this.transform.position, food.transform.position);
                nearestfood = null;
                if (dist < 16)
                {
                    nearestfood = food;
                    target = nearestfood.transform;
                }
            }
        }

    }

    /*public void RemoveEnemy(int script)
    {
        if (empty.HP <= 0)
        {
            GameManager empty = GetComponent<GameManager>();
            empty.enemies.RemoveAt(script);
        }
    }*/

    protected override void AttemptMove<T>(int xDir, int yDir)
    {
        empty = GetComponent<Statistics>();
        empty.HP--;
        //Call the AttemptMove function from MovingObject.
        base.AttemptMove<T>(xDir, yDir);
    }

    //MoveEnemy is called by the GameManger each turn to tell each Enemy to try to move towards the player.
    public void MoveEnemy()
    {
            //Declare variables for X and Y axis move directions, these range from -1 to 1.
            //These values allow us to choose between the cardinal directions: up, down, left and right.
            int xDir = 0;
            int yDir = 0;

            //If the difference in positions is approximately zero (Epsilon) do the following:
            if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon)

                //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).
                yDir = target.position.y > transform.position.y ? 1 : -1;


            //If the difference in positions is not approximately zero (Epsilon) do the following:
            else
                //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).
                xDir = target.position.x > transform.position.x ? 1 : -1;

            //Call the AttemptMove function and pass in the generic parameter Player, because Enemy is moving and expecting to potentially encounter a Player
            AttemptMove<Food>(xDir, yDir);

    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if(other.tag == "Food")
        {
            other.gameObject.SetActive(false);
        }
    }

    protected override void OnCantMove<T>(T component)
    {

    }
}
#pragma warning disable 0414
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Statistics : MonoBehaviour
{
    Text targetText;

    int Level;//レベル
    string Ability;//特性
    public int HP;
    int Attack;//こうげき
    int Defense;//ぼうぎょ
    int Sp_Atk;//とくこう
    int Sp_Def;//とくぼう
    public int Speed;//すばやさ
    int Evasion;//回避
    int Accuracy;//命中
    string Gender;//性別
    int Exp;//経験値
    int Individual_Value;//個体値
    string ATKorSPATK;

    int OK = 0;
    bool OK_Click = true;

    Text SpeciesText;
    string Text;
    Image targetImage;

    GameObject targetObj;

    // ラジアン変数
    private float rad;
    // 現在位置を代入する為の変数
    private Vector2 Position;

    public Vector2 speed = new Vector2(0.01f, 0.01f);

    private void Awake()
    {
        SpeciesText = GameObject.Find("SpeciesText").GetComponent<Text>();
        Text = "Species:";
        SpeciesText.text = Text;
        targetImage = GameObject.Find("SpeciesImage").GetComponent<Image>();
        targetText = GameObject.Find("StatsText").GetComponent<Text>();
        Generate_Stats();
        targetText.enabled = false;
    }

    private void Update()
    {
        targetText.text = "Level      " + Level.ToString() + "\n" + "Ability    " + Ability + "\n" + "Gender     " + Gender + "\n" + "IV         " + Individual_Value.ToString() + "\n" +
                    "HP         " + HP.ToString() + "\n" + "Attack     " + Attack.ToString() + "\n" + "Defense    " + Defense.ToString() + "\n" +
                    "Sp_Atk     " + Sp_Atk.ToString() + "\n" + "Sp_Def     " + Sp_Def.ToString() + "\n" + "Speed      " + Speed.ToString() + "\n" +
                    "Evasion    " + Evasion.ToString() + "\n" + "Accuracy   " + Accuracy.ToString() + "\n" + "Exp        " + Exp.ToString() + "\n" + "ATK Type   " + ATKorSPATK;

        if (HP <= 0)
        {
            Enemy enemy = GetComponent<Enemy>();
            enemy.enabled = false;
            GameObject obj = this.gameObject;
            GameObject.Destroy(obj);
            obj = null;
        }
    }
//省略


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したときに、エラーを回避出来るかが分かりませんでした。

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

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

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

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

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

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

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

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

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

check解決した方法

0

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Enemy : MovingObject
{
    public Transform target;
    private GameObject nearestfood;
    private float dist;
    private Statistics empty;

    // Use this for initialization
    protected override void Start()
    {
        GameManager.instance.AddEnemyToList(this);

        GameObject[] foods = GameObject.FindGameObjectsWithTag("Food");
        foreach(GameObject food in foods)
        {
            Vector3 foodPos = food.transform.position;
            dist = Vector3.Distance(this.transform.position, food.transform.position);
            nearestfood = null;
            if(dist < 16)
            {
                nearestfood = food;
                target = nearestfood.transform;
            }
        }

        //target = GameObject.FindGameObjectWithTag("Food").transform;
        base.Start();

        empty = GetComponent<Statistics>();
    }

    private void Update()
    {
        if(gameObject.transform.position == target.transform.position)
        {
            GameObject[] foods = GameObject.FindGameObjectsWithTag("Food");
            foreach (GameObject food in foods)
            {
                Vector3 foodPos = food.transform.position;
                dist = Vector3.Distance(this.transform.position, food.transform.position);
                nearestfood = null;
                if (dist < 16)
                {
                    nearestfood = food;
                    target = nearestfood.transform;
                }
            }
        }

        if(empty.HP <= 0)
        {
            GameManager.instance.RemoveEnemyToList(this);
            Destroy(gameObject);
        }
    }

    /*public void RemoveEnemy(int script)
    {
        if (empty.HP <= 0)
        {
            GameManager empty = GetComponent<GameManager>();
            empty.enemies.RemoveAt(script);
        }
    }*/

    protected override void AttemptMove<T>(int xDir, int yDir)
    {
        empty = GetComponent<Statistics>();
        empty.HP--;
        //Call the AttemptMove function from MovingObject.
        base.AttemptMove<T>(xDir, yDir);
    }

    //MoveEnemy is called by the GameManger each turn to tell each Enemy to try to move towards the player.
    public void MoveEnemy()
    {
            //Declare variables for X and Y axis move directions, these range from -1 to 1.
            //These values allow us to choose between the cardinal directions: up, down, left and right.
            int xDir = 0;
            int yDir = 0;

            //If the difference in positions is approximately zero (Epsilon) do the following:
            if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon)

                //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).
                yDir = target.position.y > transform.position.y ? 1 : -1;


            //If the difference in positions is not approximately zero (Epsilon) do the following:
            else
                //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).
                xDir = target.position.x > transform.position.x ? 1 : -1;

            //Call the AttemptMove function and pass in the generic parameter Player, because Enemy is moving and expecting to potentially encounter a Player
            AttemptMove<Food>(xDir, yDir);

    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        if(other.tag == "Food")
        {
            other.gameObject.SetActive(false);
        }
    }

    protected override void OnCantMove<T>(T component)
    {

    }

}


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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/15 01:55

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

    キャンセル

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

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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

  • C#

    7119questions

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

  • Unity

    4004questions

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

  • Unity2D

    884questions