[유니티]List에 저장된 게임 오브젝트를 안전하고 최적화된 방법으로 파괴하는 방법
게임 개발 중, 동적으로 생성된 오브젝트를 파괴하고 메모리를 정리하는 작업은 매우 중요합니다. 특히 유니티(Unity)에서는 스크립트(Script)와 게임 오브젝트(GameObject)를 관리할 때, 잘못된 메모리 관리로 인해 성능 저하나 메모리 누수가 발생할 수 있습니다.
이번 포스팅에서는 List<Script>에 저장된 스크립트와 관련된 게임 오브젝트를 최적화된 방법으로 **파괴(Destroy)**하고, 리스트를 정리하는 방법에 대해 알아보겠습니다.
1. 문제 상황
예를 들어, 우리가 게임 중 동적으로 생성한 오브젝트의 스크립트를 List<Script>에 저장했다고 가정해 봅시다. 게임이 진행되면서 특정 조건에서 이 오브젝트를 "파괴(Destroy)"하고 리스트를 정리(Clear)해야 할 필요가 있습니다.
다음과 같은 작업이 필요합니다:
- 스크립트와 연결된 게임 오브젝트를 파괴 (메모리에서 해제).
- 리스트를 초기화 및 정리 (메모리 최적화).
- 파괴 중 이미 파괴된 오브젝트나 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>에 저장된 스크립트와 관련된 게임 오브젝트를 효율적으로 파괴하고 메모리를 최적화하는 방법을 살펴보았습니다. 유니티 프로젝트에서는 메모리 관리를 소홀히 하면 성능 저하, 메모리 누수, 그리고 충돌 등의 문제가 발생할 수 있으므로, 항상 효율적인 리소스 관리 방법을 고민해야 합니다.