
유니티를 개발하다 보면 배열이나 리스트를 다루는 일이 많죠? GameObject들을 찾고, 필터링하고, 정렬하고... 이런 작업들을 반복문으로 하다 보면 코드가 길어지고 복잡해집니다.
그런데 LINQ를 알고 나면 이런 작업들이 놀라울 정도로 간단해져요! 오늘은 유니티 개발자 관점에서 LINQ를 어떻게 활용할 수 있는지 알아보겠습니다.
Language Intergrated Query의 약자로 C#에서 컬렉션 데이터를 쉽게 다룰 수 있게 해주는 기능입니다. SQL처럼 데이터를 쿼리할 수 있어서 코드가 훨씬 직관적이고 간결해져요.
using System.Linq; // 추가!!
기존 방식과 LINQ를 비교하며 공부해봅시다.
// Enemy 리스트에서 이름만 가져와 리스트로 만들기
// 기존 방식
List<string> enemyNames = new List<string>();
foreach (GameObject enemy in enemies)
{
enemyNames.Add(enemy.name);
}
// LINQ 방식
var enemyNames = enemies.Select(enemy => enemy.name).ToList();
// 활성화된 적들만 찾기
// 기존 방식
List<GameObject> activeEnemies = new List<GameObject>();
foreach(GameObject enemy in allEnemies)
{
if(enemy.activeInHierarchy)
{
activeEnemies.Add(enemy);
}
}
// LINQ 방식
var activeEnemies = allEnemies.Where(enemy => enemy.activeInHierarchy).ToList();
// 특정 거리 내의 적들 찾기 (LINQ 방식)
var nearbyEnemies = enemies.Where(enemy =>
Vector3.Distance(transform.position, enemy.transform.position) < 10f
).ToList()
// 거리순으로 정렬
// 내림차순 정렬은 `OrderByDescending`
var sortedEnemies = enemies.OrderBy(enemy =>
Vector3.Distance(transform.position, enemy.transform.position)
).ToList();
// 가장 가까운 적 찾기
// 1. Distance 사용
var closestEnemy = enemies.OrderBy(enemy =>
Vector3.distance(transform.position, enemy.transform.position)
).FirstOrDefault();
// 2. sqrMagnitude 사용 (제곱근 계산 생략으로 성능 향상)
var closestEnemy = enemies.OrderBy(enemy =>
(transform.position - enemy.transform.position).sqrMagnitude
).FirstOrDefault();
// 모든 적 오브젝트 중 활성화되고 HP가 50 이상인 것들
var dangerousEnemies = GameObject.FindGameObjectsWithTag("Enemy")
.Where(enemy => enemy.activeInHierarchy)
.Where(enemy => enemy.GetComponent<EnemyHealth>().currentHP >= 50)
.OrderByDescending(enemy => enemy.Getcomponent<EnemyHealth>().currentHP)
.ToList();
// 모든UI 버튼을 한 번에 비활성화
GetComponentsInChildren<Button>()
.ToList()
.ForEach(btn => btn.interactable = false);
// 아이템 타입별 개수 세기
var itemCounts = inventory
.Where(item => item.itemType)
.GroupBy(item => item.itemType)
.ToDictionary(g => g.Key, g => g.Count());
// 판매 가능한 아이템들의 총 가치
var totalSellValue = inventory
.Where(item => item.canSell)
.Sum(item => item.sellPrice);
// 무기만 필터링해서 공격력 순으로 정렬
var sortedWeapons = inventory
.Where(item => item.itemType == ItemType.Weapon)
.OrderByDescending(Weapon => weapon.attackPower)
.ToList();
// 리더보드 생성 (상위 10명)
var leaderboard = playerScores
.OrderByDescending(score => score.points)
.Take(10)
.Select((score, index) => new {
Rank = index + 1,
Name = score.playerName,
Points = score.points
})
.ToList();
// 평균 점수보다 높은 플레이어들
var averageScore = playerScores.Average(score => score.points);
var aboveAverageUsers = playerScores
.Where(score => score.points > averageScore)
.ToList();
// 언락된 레벨들만 난이도순으로
var availableLevels = allLevels
.Where(level => level.isUnlocked)
.OrderBy(level => level.difficulty)
.ToList();
// 클리어하지 못한 레벨 중 가장 쉬운 것
var nextLevel = allLevels
.Where(level => level.isUnlocked && !level.isCompleted)
.OrderBy(level => level.difficulty)
.FirstOrDefault();
// 활성화된 적들 중에서 플레이어 근처에 있고,
// HP가 낮은 순으로 정렬해서 상위 3마리만 가져오기
var targetEnemies = GameObject.FindGameObjectsWithTag("Enemy")
.Where(enemy => enemy.activeInHierarchy)
.Where(enemy => Vector3.Distance(player.position, enemy.transform.position) < 15f
.Select(enemy => enemy.GetComponent<Enemy>())
.Where(enemy => enemy != null)
.OrderBy(enemy => enemy.currentHP)
.Take(3)
.ToList();
// 적들을 타입별로 그룹화
var enemyGroups = enemies
.GroupBy(enemy => enemy.GetComponent<Enemy>().enemyType)
.ToDictionary(g = g.Key, g => g.ToList());
// 아이템을 등급별로 개수 세기
var itemsByRarity = inventory
.Where(item => item != null)
.GroupBy(item => item.rarity)
.Select(g => new {
Rarity = g.Key,
Count = g.Count(),
TotalValue = g.Sum(item => item.value)
})
.ToList();
// 게임 시작 시나 필요할 때만
void Start() {
enemies = GameObject.FindGameObjectsWithTag("Enemy")
.Where(e => e.activeInHierarchy)
.ToList();
}
// 이벤트 발생 시
void OnEnemyDeath() {
var remainingEnemies = enemies.Where(e => e != null).ToList();
}
// Update에서 매 프레임마다 - 성능 저하!
void Update() {
var nearbyEnemies = FindObjectsOfType<Enemy>()
.Where(e => Vector3.Distance(transform.position, e.transform.position) < 10f)
.ToList();
}
// 캐싱 예시
private List<Enemy> cachedEnemies;
void Start() {
RefreshEnemyCache();
}
void RefreshEnemyCache()
{
cachedEnemies = FindObjectsOfType<Enemy>().ToList();
}
void UpdateEnemyLogic()
{
var activeEnemies = cachedEnemies
.Where(enemy => enemy != null && enemy.gameObject.activeInHierarchy)
.ToList();
}
게임 개발뿐만 아니라 다른 부분에서도 LINQ는 정말 강력합니다.
// 배열에서 중복 제거
var unique = numbers.Distinct().ToArray();
// 두 배열의 교집합
var common = arr1.Intersect(arr2).ToArray();
// 문자열에서 각 문자의 빈도수
var frequency = text.GroupBy(c => c)
.ToDictionary(g => g.Key, g => g.Count());
// k번째로 큰 수
var kthLargest = numbers.OrderByDescending(x => x)
.Distinct()
.Skip(k - 1)
.First();
LINQ를 익혀두면 유니티 개발이 정말 편해집니다. 복잡한 반복문 대신 한 줄로 해결되는 경우가 많아서 코드도 깔끔해지고 버그도 줄어들어요!
처음에는 낯설 수 있지만, 몇 번 써보면 없어서는 안 될 도구가 될 거예요. 특히 데이터 처리나 GameObject 관리할 때 정말 유용하니까 꼭 익혀두시길 추천합니다!