https://waterglass0105.tistory.com/110
Tag 내용을 정리하면서, 지나친 Tag 사용, 잦은 Tag 기반 검색이 시스템에 좋지 않은 영향을 줄 수 있다고 언급하며 다른 검색 방법에 대해 소개한 바 있다. 이번에는 FindWithTag나 FindGameObjectsWithTag 대신 사용할 수 있는 대체 검색 방법들에 대해 알아보고자 한다.
1. Layer 기반 검색
https://waterglass0105.tistory.com/111
특정 게임 오브젝트를 레이어로 구분한 후, 물리적 충돌 검사나 Physics 클래스를 활용하여 검색한다.
방법
- 오브젝트에 특정 레이어를 설정하고, Physics.OverlapSphere, Physics.Raycast 등을 활용해 레이어 마스크를 사용한 필터링을 한다.
장점
- 특정 영역 내의 오브젝트를 효율적으로 검색 가능.
- 물리 연산 기반으로 필요한 데이터만 가져옴.
int layerMask = LayerMask.GetMask("Enemy");
Collider[] colliders = Physics.OverlapSphere(transform.position, searchRadius, layerMask);
foreach (Collider collider in colliders)
{
Debug.Log(collider.gameObject.name);
}
위 코드와 같이, layerMask를 설정하고, Collider에 해당 layerMask와의 충돌을 검사하는 로직을 만들 수 있다.
2. 오브젝트 참조를 미리 저장
게임 오브젝트의 참조를 스크립트나 관리 클래스에서 미리 저장해두고 필요할 때 사용한다.
방법
- Start나 Awake에서 필요한 오브젝트를 한 번 검색 후 캐싱.
- 또는 Unity Editor를 통해 직접 참조를 드래그 앤 드롭 방식으로 연결.
장점
- 검색을 매번 수행할 필요가 없어 매우 빠르고 효율적.
- 자주 사용되는 오브젝트의 경우 특히 유용.
public GameObject player;
void Start()
{
player = GameObject.FindWithTag("Player");
}
위 코드와 같이, Start 혹은 Awake에 필요한 오브젝트를 미리 캐싱하여 사용하면 빠르고 효율적으로 탐색이 가능하다. 하지만 이 경우, 런타임에 동적으로 생성된 객체에 대한 참조를 위해서는 별도의 로직이 요구된다.
3. Dictionary 또는 List를 활용한 관리
오브젝트를 직접 관리하는 컬렉션을 만들어 필요할 때 조회한다.
방법
- 특정 오브젝트들이 생성/삭제될 때마다 리스트나 딕셔너리에 추가/제거.
- 검색 시 컬렉션에서 바로 접근.
장점
- O(1) 또는 O(n) 수준의 검색 속도를 제공.
- 복잡한 검색 조건도 쉽게 처리 가능.
public static List<GameObject> enemies = new List<GameObject>();
void AddEnemy(GameObject enemy)
{
enemies.Add(enemy);
}
void RemoveEnemy(GameObject enemy)
{
enemies.Remove(enemy);
}
GameObject FindClosestEnemy(Vector3 position)
{
GameObject closest = null;
float closestDistance = Mathf.Infinity;
foreach (GameObject enemy in enemies)
{
float distance = Vector3.Distance(position, enemy.transform.position);
if (distance < closestDistance)
{
closestDistance = distance;
closest = enemy;
}
}
return closest;
}
4. Parent-Child 관계 활용
특정 오브젝트의 자식 오브젝트만 검색하여 범위를 줄인다.
방법
- transform.Find 또는 GetComponentsInChildren을 사용.
- 특정 부모 아래의 자식 오브젝트만 대상.
장점
- 검색 범위를 크게 줄일 수 있음.
- 계층 구조를 활용하면 검색이 더 효율적.
Transform child = transform.Find("ChildName");
if (child != null)
{
Debug.Log(child.gameObject.name);
}
5. ScriptableObject로 데이터 관리
ScriptableObject를 사용하여 오브젝트 데이터나 참조를 중앙에서 관리한다.
방법
- 게임 내 모든 오브젝트의 데이터를 ScriptableObject로 유지.
- 이를 통해 필요한 오브젝트에 접근.
장점
- 데이터 중심으로 관리가 가능해 확장성이 뛰어남.
- 복잡한 오브젝트 간 관계를 쉽게 설정 가능.
[CreateAssetMenu(fileName = "EnemyManager", menuName = "Managers/EnemyManager")]
public class EnemyManager : ScriptableObject
{
public List<GameObject> enemies;
public void AddEnemy(GameObject enemy)
{
if (!enemies.Contains(enemy))
enemies.Add(enemy);
}
public void RemoveEnemy(GameObject enemy)
{
if (enemies.Contains(enemy))
enemies.Remove(enemy);
}
}
6. Events & Delegates 활용
이벤트나 델리게이트를 사용하여 특정 상황에서만 필요한 오브젝트를 가져온다.
방법
- 오브젝트 상태가 변경될 때 이벤트를 호출하여 필요한 오브젝트만 등록.
장점
- 필요할 때만 처리되어 불필요한 연산을 줄임.
public delegate void OnEnemyAdded(GameObject enemy);
public static event OnEnemyAdded EnemyAdded;
public static void AddEnemy(GameObject enemy)
{
EnemyAdded?.Invoke(enemy);
}
6-1. Collider 및 Physics 활용
동적 객체가 특정 범위 내에 있거나 특정 조건에 부합하는 경우, Physics.OverlapSphere 또는 Collider 이벤트를 활용해 탐색할 수 있다.
int enemyLayerMask = LayerMask.GetMask("Enemy");
Collider[] enemiesInRange = Physics.OverlapSphere(player.transform.position, 10f, enemyLayerMask);
foreach (Collider collider in enemiesInRange)
{
Debug.Log($"Enemy in range: {collider.gameObject.name}");
}
이처럼, 게임의 구조와 요구사항에 따라 적절한 방법을 선택하여 성능 최적화를 도모할 수 있다.
특히, 자주 검색이 필요한 오브젝트는 참조 캐싱 또는 리스트/딕셔너리 관리를 적극적으로 활용하는 것이 가장 권장된다.
이 밖에도 다양한 검색 방법이 존재하지만 이 포스트에는 자리가 부족하여 남기지 않는다.
'Study > Unity Engine' 카테고리의 다른 글
[Unity] DLL과 PDB (0) | 2025.01.18 |
---|---|
[Unity] 컴파일 과정 (0) | 2025.01.18 |
[Unity] ScriptableObject (0) | 2025.01.18 |
[Unity] Layer (0) | 2025.01.18 |
[Unity] Tag (0) | 2025.01.17 |