"열차에 불이 났다. 살아나갈 수 없을 것 같으니 꿋꿋하게 살아라." – 지하철 안, 어머니 최금자 씨가 아들에게

타닥타닥 카테고리에 있는 글

2013/05/31

Destroy와 DestroyImmediate

유니티 2버전부터 지금까지 쓰면서 설명 대충 보고 쓰다가 잘못 알고 있는 걸 이번에 제대로 알게 돼서 적어둡니다. 먼저 함수 설명을 봅시다.

+2022.7.24 > 최신 내용으로 갱신


원문

https://docs.unity3d.com/ScriptReference/Object.Destroy.html

Object.Destroy

선언

public static void Destroy( Object obj, float t = 0.0f );

매개변수

obj파괴할 객체.
t객체를 파괴하기 전에 지연할 시간 값.

설명

GameObject, component나 asset을 제거합니다.

객체 obj는 현재 Update 루프가 끝난 직후이거나 시간이 지정된 경우 호출 후 t초가 지난 뒤 바로 파괴됩니다. objComponent인 경우 GameObject에서 Component를 제거한 후 파괴합니다. objGameObject인 경우 GameObject와 모든 Component들 그리고 모든 자식 객체를 파괴합니다. 실제 객체 파괴는 항상 현재 Update 루프가 끝나기 전까지 지연되지만, 렌더링 전에는 항상 완료됩니다.

알림: MonoBehaviour 스크립트를 파괴하면 OnDisable과 OnDestroy가 스크립트 제거 전에 호출됩니다.

using UnityEngine;

public class ScriptExample : MonoBehaviour
{
    void DestroyGameObject()
    {
        Destroy( gameObject );
    }

    void DestroyScriptInstance()
    {
        // GameObject에서 이 스크립트의 인스턴스를 제거합니다
        Destroy( this );
    }

    void DestroyComponent()
    {
        // GameObject에서 Rigidbody를 제거합니다
        Destroy( GetComponent<Rigidbody>() );
    }

    void DestroyObjectDelayed()
    {
        // 객체를 불러오고 5초후 GameObject를 제거합니다
        Destroy( gameObject, 5 );
    }

    // 사용자가 Fire1 버튼을 누르면 GameObject에서 BoxCollider component를 제거합니다
    void Update()
    {
        if (Input.GetButton( "Fire1" ) && GetComponent<BoxCollider>())
        {
            Destroy( GetComponent<BoxCollider>() );
        }
    }
}

Destroy는 UnityEngine.Object 기본 클래스에서 상속되었습니다.


원문

https://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html

Object.DestroyImmediate

선언

public static void DestroyImmediate( Object obj, bool allowDestroyingAssets = false );

매개변수

obj파괴할 객체.
allowDestroyingAssetstrue로 설정하면 Asset 파괴를 허용합니다.

설명

즉시 객체 obj를 파괴합니다. 대신 Destroy를 사용할 것을 강력히 권합니다.

편집 모드에서는 지연 파괴가 호출되지 않기 때문에 이 함수는 편집기 코드를 작성할 때만 사용해야 합니다. 게임 코드에서는 대신 Object.Destroy를 사용해야 합니다. Destroy는 항상 지연됩니다. (하지만 같은 프레임 내에 실행됩니다) 이 함수는 Asset을 영구적으로 파괴할 수 있으니 사용 시 주의해야 합니다! 또한, 절대로 배열을 돌면서 돌고 있는 배열요소를 파괴하면 안 된다는 것을 알아야 합니다. 이는 심각한 문제를 일으킬 수 있습니다. (유니티뿐만 아니라 일반 프로그래밍에서도 적용되는 이야기. 배열을 순회하는 도중 배열 요소를 지워버리면 반복문은 현재 남은 배열 수와 상관없이 남은 횟수를 마저 돌게 되고, 지워버린 곳의 쓰레깃값이나 잘못된 주소를 참조하게 되어 예외가 발생합니다.)


위에 나온 대로 DestroyImmediate는 주로 편집 모드(플레이 버튼을 눌러서 게임이 실행 중인 게 아니라 그냥 유니티를 켠 상태)에서만 써야 합니다. 하지만 게임 모드(런타임, 게임이 실행 중)에서 써도 크게 문제 되지는 않는 것으로 보입니다. (위에 설명에서도 안 쓰는 걸 권하고 있고 상황을 전부 테스트해본 게 아니라서 확답은 아닙니다.)

둘의 차이는 첫 번째로 파괴의 시점입니다. Destroy는 당장 파괴가 되지 않습니다. 다시 말해서 Destroy가 불린 Update 프레임 내에서 파괴한 객체를 다시 불러도 해당 객체는 null이 아니라는 이야기. 해당 객체는 Update 프레임이 끝나야 파괴됩니다. 반대로 DestroyImmediate는 불린 즉시 객체가 파괴되며 해당 객체는 null이기 때문에 다시 부르면 Null참조예외가 일어납니다.

두 번째 차이는 Asset 파괴 가능 유무입니다. 일단 먼저 말하지만 Asset(텍스처, 오디오 파일 등등)은 게임 모드일 때 파괴(메모리 해제가 아니고 진짜 파괴.) 하는 게 아닙니다! Destroy나 DestroyImmediate 둘 다 런타임 때 Asset에 사용하면 다음과 같은 에러가 발생합니다.

데이터 유실을 피하고자 Asset 파괴는 허용하지 않습니다.
정말로 Asset 파괴를 원하는 경우 DestroyImmediate( Object, true );를 사용하세요.

위에 설명과 에러에서는 DestroyImmediate로 파괴하면 실제로 파일이 삭제될 것 같은 뉘앙스인데 물리디스크에서 삭제되는 것은 아닙니다. 파괴 후 Project탭을 보면 파일이 보이긴 하나 클릭해서 Inspector탭을 보면 정보가 아무것도 안 나오며 사용도 안 되고 없는 파일 취급합니다. 유니티를 다시 켜거나 해당 폴더를 Reimport 하면 파일 이름만 보이고요. 다시 파일을 사용하려면 meta 파일을 사용하는 경우 meta 파일을 지우고 유니티를 띄워 다시 유니티가 meta 파일을 생성하게 해서 목록을 갱신시키거나 meta 파일을 사용하지 않는 경우 파일을 다른 곳으로 옮긴 후 유니티를 띄워서 목록을 갱신시키고 파일을 다시 원래 있던 곳으로 넣은 후 유니티를 띄워 목록을 다시 갱신시키면 됩니다.

마지막으로 함수 사용 목적이 사용하지 않는 Asset을 메모리에서 내리기(해제하기) 위한 것이라면 Resources.UnloadAsset을 사용해 해당 Asset을 직접 내리거나 Resources.UnloadUnusedAssets를 사용해서 사용하지 않는 Asset 모두를 내리면 됩니다.


참조

https://docs.unity3d.com/ScriptReference/Object.Destroy.html
https://docs.unity3d.com/ScriptReference/Object.DestroyImmediate.html

댓글 남기기 | cat > 타닥타닥 | tag >

예전 글

최근 글