GameDev/[Unity]

[유니티]List에 저장된 게임 오브젝트를 안전하고 최적화된 방법으로 파괴하는 방법

Bit by Bit 2025. 1. 8. 23:11
728x90

 게임 개발 중, 동적으로 생성된 오브젝트를 파괴하고 메모리를 정리하는 작업은 매우 중요합니다. 특히 유니티(Unity)에서는 스크립트(Script)와 게임 오브젝트(GameObject)를 관리할 때, 잘못된 메모리 관리로 인해 성능 저하나 메모리 누수가 발생할 수 있습니다.

이번 포스팅에서는 List<Script>에 저장된 스크립트와 관련된 게임 오브젝트를 최적화된 방법으로 **파괴(Destroy)**하고, 리스트를 정리하는 방법에 대해 알아보겠습니다.


1. 문제 상황

예를 들어, 우리가 게임 중 동적으로 생성한 오브젝트의 스크립트를 List<Script>에 저장했다고 가정해 봅시다. 게임이 진행되면서 특정 조건에서 이 오브젝트를 "파괴(Destroy)"하고 리스트를 정리(Clear)해야 할 필요가 있습니다.

다음과 같은 작업이 필요합니다:

  1. 스크립트와 연결된 게임 오브젝트를 파괴 (메모리에서 해제).
  2. 리스트를 초기화 및 정리 (메모리 최적화).
  3. 파괴 중 이미 파괴된 오브젝트나 null 참조 문제 방지.

2. 최적화된 코드 구현

다음은 위 문제를 해결하기 위한 최적화된 코드 예제입니다:

using System.Collections.Generic;
using UnityEngine;

public class ScriptDestroyer : MonoBehaviour
{
    // Script 리스트
    public List<Script> ObjectScript = new List<Script>();

    // 리스트의 모든 Script와 관련된 게임 오브젝트를 파괴하고 리스트를 정리하는 함수
    public void DestroyAllAndClear()
    {
        // 모든 Script의 게임 오브젝트를 파괴
        foreach (var script in ObjectScript)
        {
            if (script != null && script.gameObject != null)
            {
                Destroy(script.gameObject);
            }
        }

        // 리스트 초기화
        ObjectScript.Clear();

        // 리스트의 내부 용량 초기화 (선택 사항)
        ObjectScript.TrimExcess();
    }
}

3. 코드 상세 설명

1) foreach 루프를 활용한 파괴

foreach 루프를 사용해 리스트 내의 모든 Script를 순회하며, 해당 스크립트에 연결된 게임 오브젝트를 Destroy()로 파괴합니다.

  • 유효성 체크:
    • 스크립트가 null이거나 이미 파괴된 오브젝트를 참조하지 않도록 안전하게 검사합니다.
    • 이를 통해 불필요한 예외(Exception)를 방지할 수 있습니다.
  • if (script != null && script.gameObject != null)
  • Destroy() 사용:
    Destroy()는 유니티에서 게임 오브젝트를 파괴하는 함수입니다. 하지만 즉시 메모리에서 제거되지 않고, 다음 프레임에서 제거됩니다. 따라서 Destroy() 이후에는 파괴된 오브젝트를 참조하지 않아야 합니다.

2) 리스트 초기화: Clear()

리스트를 비우기 위해 Clear()를 호출합니다.

  • 이 함수는 리스트의 모든 요소를 제거하지만, 내부 메모리 용량(캐패시티)은 그대로 유지합니다.
  • 따라서, 큰 리스트의 메모리를 해제하려면 아래의 **TrimExcess()**를 추가로 호출해야 합니다.

3) 메모리 최적화: TrimExcess()

리스트의 내부 메모리 용량을 초기화합니다.

  • ObjectScript.TrimExcess();는 리스트의 크기를 현재 사용 중인 요소 수에 맞게 조정합니다.
  • 리스트의 크기가 매우 크거나 많은 오브젝트를 다루는 상황에서 메모리 낭비를 줄일 수 있습니다.

4. 추가 최적화 방법: 오브젝트 풀링(Pooling) 활용

만약 리스트에 있는 오브젝트가 자주 생성 및 파괴된다면, "오브젝트 풀링(Pooling)"을 사용하는 것이 좋습니다.
오브젝트 풀링은 미리 생성된 오브젝트를 재활용해 파괴 및 생성 과정을 최소화하는 방식으로, 성능을 크게 향상시킬 수 있습니다.

public void ReturnToPool(GameObject obj)
{
    obj.SetActive(false); // 게임 오브젝트 비활성화
    pool.Add(obj);        // 풀에 반환
}

public GameObject GetFromPool()
{
    if (pool.Count > 0)
    {
        GameObject obj = pool[0];
        pool.RemoveAt(0);
        obj.SetActive(true); // 활성화 후 반환
        return obj;
    }

    // 풀에 오브젝트가 없으면 새로 생성
    GameObject newObj = Instantiate(prefab);
    return newObj;
}

5. 최적화된 메모리 관리의 중요성

  • 안전성:
    • 이미 파괴된 오브젝트를 참조하거나 null 참조 문제를 방지할 수 있습니다.
  • 성능 최적화:
    • 게임 내 불필요한 메모리 점유를 줄이고, 메모리 릭(Memory Leak)을 예방할 수 있습니다.
  • 확장성:
    • List와 같은 자료 구조를 효율적으로 관리하여 대규모 오브젝트 관리에도 대응할 수 있습니다.

List<Script>에 저장된 스크립트와 관련된 게임 오브젝트를 효율적으로 파괴하고 메모리를 최적화하는 방법을 살펴보았습니다. 유니티 프로젝트에서는 메모리 관리를 소홀히 하면 성능 저하, 메모리 누수, 그리고 충돌 등의 문제가 발생할 수 있으므로, 항상 효율적인 리소스 관리 방법을 고민해야 합니다.

728x90