• トップ
  • ブログ一覧
  • 【Unity】Destroyメソッドでオブジェクトが破棄されるタイミング
  • 【Unity】Destroyメソッドでオブジェクトが破棄されるタイミング

    やまちゃん(エンジニア)やまちゃん(エンジニア)
    2024.03.01

    IT技術

    Destroyメソッドの使い方について

    Unityでゲームを作る際、例えばキャラクターが弾を撃ち、敵を倒す場合、弾が敵に当たった時に弾のオブジェクトをシーン上から削除する必要が出てくるだろう。
    使い方は以下の通り。

    1
    2// 特定のオブジェクトを破棄する
    3GameObject bullet = GameObject.Find("Bullet");
    4Destroy(bullet);
    5
    6// このスクリプトがついたオブジェクトを破棄する
    7Destroy(gameObject);

    ただし、Destroyはコンポーネントも削除できるので、書き方によってはオブジェクトは残ってしまう

    1
    2// このスクリプトを破棄する(オブジェクトは残る)
    3Destroy(this);
    4
    5var canvasGroup = gameObject.GetComponent();
    6//  CanvasGroupのコンポーネントを破棄する(オブジェクトは残る)
    7Destroy(canvasGroup);

    Destroyメソッドでオブジェクトが破棄されるタイミング

    本題のDestroyメソッドでオブジェクトが破棄されるタイミングは、
    Destroyメソッドが呼ばれたフレームの最後
    ライフサイクルのOnDestroyに当たる部分になる。

    https://docs.unity3d.com/ja/2023.2/Manual/ExecutionOrder.html

    実際に検証

    コード:

    1using UnityEngine;
    2
    3コード
    4public class Gun : MonoBehaviour
    5{
    6    GameObject bullet;
    7    int frameCount;
    8
    9    void Start()
    10    {
    11         bullet = GameObject.Find("Bullet");
    12         frameCount = 0;
    13    }
    14
    15    void Update()
    16    {
    17        frameCount += 1;
    18        if (bullet != null)
    19        {
    20            // bulletがまだあったら破棄する
    21            Destroy(bullet);
    22        }
    23        
    24        // Destroy後に判定する
    25        if (bullet == null)
    26        {
    27            Debug.Log($"{frameCount}フレーム目: bullet is null");
    28        }
    29        else
    30        {
    31            Debug.Log($"{frameCount}フレーム目: bullet is not null");
    32        }
    33    }
    34}

    実行結果:

    Destroyの後のif文が、1フレーム目はbulletがnullじゃない、2フレーム目以降はbulletがnullという判定になっていることが確認できる。

    処理が多くなった際に、破棄したと思ったオブジェクトがDestroyメソッドと同一フレームでまだ破棄されておらず、オブジェクトの数にカウントされたりif文が想定通り判定されなかったりするので、注意が必要だ。

    対策としてはnullを代入するなどが考えられる

    1using UnityEngine;
    2
    3public class Gun : MonoBehaviour
    4{
    5    GameObject bullet;
    6    int frameCount;
    7
    8    void Start()
    9    {
    10         bullet = GameObject.Find("Bullet");
    11         frameCount = 0;
    12    }
    13
    14    void Update()
    15    {
    16        frameCount += 1;
    17        if (bullet != null)
    18        {
    19            // bulletがまだあったら破棄する
    20            Destroy(bullet);
    21            // 破棄したらその場でnullにする
    22            bullet = null;
    23        }
    24        
    25        // Destroy後に判定する
    26        if (bullet == null)
    27        {
    28            Debug.Log($"{frameCount}フレーム目: bullet is null");
    29        }
    30        else
    31        {
    32            Debug.Log($"{frameCount}フレーム目: bullet is not null");
    33        }
    34    }
    35}

    また、DestroyImmediateという即座に破棄されるメソッドもあるが、公式は推奨していないので、可能な限りDestroyを使おう。

    やまちゃん(エンジニア)

    やまちゃん(エンジニア)

    おすすめ記事